线程的同步问题

硅谷探秘者 7638 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基础 3301 1.什么是线通信线之间通信两个基本是互斥和线是指线之间所具有一种制约关系,一个线执行依赖另一个线消息,当它没有得到另一个线消息时应等待,直到消息到达时才被唤醒
official 1010 ,因此“写数据”和“读数据”两个操作执行先后顺序是不确定。而实际应用中,又必须按照“写数据→读数据”顺序来执行。如何解决这种异,就是“进”所讨论内容。亦称直接制约关系,它是指为
java基础 2078 一、什么是countDownlatchCountDownLatch是一个工具类,它通过一个计数器来实现,初始值为线数量。每当一个线完成了自己任务,计数器值就相应得减1。当计数器到达0
weblog 1295 根本区别:进是操作系统资源分配基本单位,而线是处理器任务调度和执行基本单位。包含关系:一个进内可以有多个线(默认有一个主线),线是进一部分,必须依赖于进而存在,不能独立存在。资源共享:进之间是不共享资源,多个线之间是共享资源,所以存在资源竞争
official 1110 线五种基本状态:初始化、可运行、运行、阻塞、终止阻塞状态分为:等待阻塞、阻塞、其他阻塞三种方式等待阻塞:调用某锁对象wait方法可使当前线进入等待阻塞状态,并且该线放弃所有资源(包括
java基础 3352 ”。线对象被创建后,其它线调用了该对象start()方法,从而来启动该线。例如,thread.start()。处于就绪状态线,随时可能被CPU调度执行。3.运行状态(Running):线获取C
official 1285 在上一节《一起学netty(6)》文章中,简要说明了用nio原生代码写一些不足和,以及netty在nio基础上大致做了那些工作。其中提到一点就是当活跃客户端数量太多,单线处理时所带
数据结构与算法 7824 八皇后,是一个古老而著名,是回溯算法典型案例。该是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于一行
归档
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  3 2022-09  2 2022-10  2 2022-12  5 2023-01  3 2023-02  1 2023-03  4 2023-04  2 2023-06  3 2023-07  4 2023-08  1 2023-10  1 2024-02  1 2024-03  1 2024-04  1 2024-08  1
标签
算法基础 linux 前端 c++ 数据结构 框架 数据库 计算机基础 储备知识 java基础 ASM 其他 深入理解java虚拟机 nginx git 消息中间件 搜索 maven redis docker dubbo vue 导入导出 软件使用 idea插件 协议 无聊的知识 jenkins springboot mqtt协议 keepalived minio mysql ensp 网络基础 xxl-job rabbitmq haproxy srs 音视频 webrtc javascript 加密算法
目录
没有一个冬天不可逾越,没有一个春天不会来临。最慢的步伐不是跬步,而是徘徊,最快的脚步不是冲刺,而是坚持。