线程的同步问题

硅谷探秘者 6514 0 0

多线程带来的问题:

        线程有时候回和其他线程共享一些资源,比如内存、数据库等。当多个线程同时读写同一份共享资源的时候,可能会发生冲突。这时候,我们就需要引入线程“同步”机制,即各位线程之间要有顺序使用,不能杂乱无章随意使用。

实例:

线程A

package threadTest.test7;
/**
 * @author LENOVO
 */
public class ThreadA implements Runnable{
	
	private String context;
	
	public ThreadA(String context) {
		super();
		this.context = context;
	}
	@Override
	public void run() {
		for(int i=0;i<context.length();i++) {
			System.out.print(context.charAt(i));
		}
		System.out.println();
	}
}

线程B

package threadTest.test7;
/**
 * @author LENOVO
 */
public class ThreadB implements Runnable{
	
	private String context;
	
	public ThreadB(String context) {
		super();
		this.context = context;
	}
	@Override
	public void run() {
		for(int i=0;i<context.length();i++) {
			System.out.print(context.charAt(i));
		}
		System.out.println();
	}
}

主线程

package threadTest.test7;
/**
 * 主线程
 * @author LENOVO
 */
public class Test1 {
	public static void main(String[] args) {
		new Thread(new ThreadA("12345678910")).start();
		new Thread(new ThreadB("abcdefghijk")).start();
	}
}

如果一般情况下分析结果应该为:

12345678910
abcdefghijk

但是某种情况下却出现:

image.png

也就是有些情况下线程A的for循环还未执行完,cpu就把资源给了线程B,结果就造成这样的问题


解决办法:

1.同步代码块:

要用同步代码块的话需要一个锁,而且两个线程必须用同一个锁,我们对上面的代码进行改进:

package threadTest.test7;
/**
 * @author LENOVO
 */
public class ThreadA implements Runnable{
	
	private String context;
	private Object obj;//充当锁,并不具有业务意义
	
	public ThreadA(String context,Object obj) {
		super();
		this.obj=obj;
		this.context = context;
	}
	@Override
	public void run() {
		synchronized(obj) {//锁住obj
			for(int i=0;i<context.length();i++) {
				System.out.print(context.charAt(i));
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		System.out.println();
	}
}
package threadTest.test7;
/**
 * @author LENOVO
 */
public class ThreadB implements Runnable{
	
	private String context;
	private Object obj;//充当锁,并不具有业务意义
	
	public ThreadB(String context,Object obj) {
		super();
		this.obj=obj;
		this.context = context;
	}
	@Override
	public void run() {
		synchronized(obj) {
			for(int i=0;i<context.length();i++) {
				System.out.print(context.charAt(i));
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		System.out.println();
	}
}
package threadTest.test7;
/**
 * 主线程
 * @author LENOVO
 */
public class Test1 {
	public static void main(String[] args) {
		Object obj=new Object();
		new Thread(new ThreadA("12345678910",obj)).start();
		new Thread(new ThreadB("abcdefghijk",obj)).start();
	}
}

为了cpu实现明显的切换效果,在每次循环中都加了Thread.sleep(1);,睡眠一毫秒

这样就能保证synchronized代码块中的代码能够执行完,而不会出现错误

image.png



2.同步方法:

synchronized 修饰方法,即锁定当前要执行的方法的对象

代码:
package threadTest.test8;

public class Node {
	private String context;
	public Node(String context) {
		super();
		this.context = context;
	}
	public void p() {
		for(int i=0;i<context.length();i++) {
			System.out.print(context.charAt(i));
			try {
				Thread.sleep(2);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println();
	}
}
package threadTest.test8;
/**
 * @author LENOVO
 */
public class ThreadA implements Runnable{
	private Node n;
	public ThreadA(Node n) {
		super();
		this.n=n;
	}
	@Override
	public void run() {
		n.p();
	}
}
package threadTest.test8;
/**
 * @author LENOVO
 */
public class ThreadB implements Runnable{
	
	private Node n;
	public ThreadB(Node n) {
		super();
		this.n=n;
	}
	@Override
	public void run() {
		n.p();
	}
}
package threadTest.test8;
/**
 * 主线程
 * @author LENOVO
 */
public class Test1 {
	public static void main(String[] args) {
		Node n=new Node("123456789");
		new Thread(new ThreadA(n)).start();
		new Thread(new ThreadB(n)).start();
	}
}

执行结果:

image.png

发现没有达到目的

那么在Node类的p方法中加 synchronized 关键字如:

public synchronized void p() {
		for(int i=0;i<context.length();i++) {
			System.out.print(context.charAt(i));
			try {
				Thread.sleep(2);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println();
	}

再次执行

image.png

达到了我们想要的目的


如果主线程改成:

package threadTest.test8;
/**
 * 主线程
 * @author LENOVO
 */
public class Test1 {
	public static void main(String[] args) {
		Node n=new Node("123456789");
		Node n2=new Node("abcdefghijk");
		new Thread(new ThreadA(n)).start();
		new Thread(new ThreadB(n2)).start();
	}
}

发现结果为

image.png

原因就是因为 同步方法锁定的是当前要执行的方法的对象,但是n和n2不是同一个对象,所以并没有达到效果


总结:

同步监视器.jpg


猜你喜欢
java基础 2355 1.什么是线通信线之间通信两个基本是互斥和线是指线之间所具有一种制约关系,一个线执行依赖另一个线消息,当它没有得到另一个线消息时应等待,直到消息到达时才被唤醒
official 251 ,因此“写数据”和“读数据”两个操作执行先后顺序是不确定。而实际应用中,又必须按照“写数据→读数据”顺序来执行。如何解决这种异,就是“进”所讨论内容。亦称直接制约关系,它是指为
weblog 473 根本区别:进是操作系统资源分配基本单位,而线是处理器任务调度和执行基本单位。包含关系:一个进内可以有多个线(默认有一个主线),线是进一部分,必须依赖于进而存在,不能独立存在。资源共享:进之间是不共享资源,多个线之间是共享资源,所以存在资源竞争
official 283 线五种基本状态:初始化、可运行、运行、阻塞、终止阻塞状态分为:等待阻塞、阻塞、其他阻塞三种方式等待阻塞:调用某锁对象wait方法可使当前线进入等待阻塞状态,并且该线放弃所有资源(包括
java基础 1355 ”。线对象被创建后,其它线调用了该对象start()方法,从而来启动该线。例如,thread.start()。处于就绪状态线,随时可能被CPU调度执行。3.运行状态(Running):线获取C
official 325 在上一节《一起学netty(6)》文章中,简要说明了用nio原生代码写一些不足和,以及netty在nio基础上大致做了那些工作。其中提到一点就是当活跃客户端数量太多,单线处理时所带
数据结构与算法 6586 八皇后,是一个古老而著名,是回溯算法典型案例。该是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于一行
java基础 1113 非守护线结束时,序也就终止了,时会杀死进所有守护线。反过来说,只要任何非守护线还在运行,序就不会终止。守护线和用户线没啥本质区别:唯一之处就在于虚拟机离开:如果用户线
归档
2018年11月  12 2018年12月  33 2019年01月  28 2019年02月  28 2019年03月  32 2019年04月  27 2019年05月  33 2019年06月  6 2019年07月  12 2019年08月  12 2019年09月  21 2019年10月  8 2019年11月  15 2019年12月  25 2020年01月  9 2020年02月  5 2020年03月  16 2020年04月  4 2020年06月  1 2020年07月  7 2020年08月  13 2020年09月  9 2020年10月  5 2020年12月  3 2021年01月  1 2021年02月  5 2021年03月  7 2021年04月  4 2021年05月  4 2021年06月  1 2021年07月  7 2021年08月  2 2021年09月  8 2021年10月  9 2021年11月  16 2021年12月  14 2022年01月  7 2022年05月  1 2022年08月  1
标签
算法基础 linux 前端 c++ 数据结构 框架 数据库 计算机基础 储备知识 java基础 ASM 其他 深入理解java虚拟机 nginx git 消息中间件 搜索 maven redis docker dubbo vue 导入导出 软件使用 idea插件 协议 无聊的知识 jenkins springboot mqtt协议 keepalived minio
目录
祝愿神州十三飞行乘组平安归来