Java高并发synchronized讲解生产者消费者
问题描述
题目:两个线程操作一个变量,实现两个线程对同一个资源一个进行加1操作,另外一个进行减1操作,且需要交替实现,变量的初始值为0。即两个线程对同一个资源进行加一减一交替操作。话不多说,开干
首先我们先定义操作的资源,并且定义方法。
首先定义资源类
//资源类 class Resource { private int number = 0; public synchronized void up() throws InterruptedException { //1.判断 if(number != 0) { this.wait(); } //2.干活 number++; System.out.println(Thread.currentThread().getName() + "\t" + number); this.notifyAll(); } public synchronized void down() throws InterruptedException { if(number == 0) { this.wait(); } number--; System.out.println(Thread.currentThread().getName() + "\t" + number); this.notifyAll(); } }
接着我们写我们的两个线程
public class ThreadWaitNotifyDemo { public static void main(String[] args) { Resource resource = new Resource(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { resource.up(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { resource.down(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "B").start(); } }
结果如下图
这里我们通过Lambda表达式来通过匿名内部类来创建线程并启动,可以看到一个线程up方法,另外一个线程进行down方法,
首先比如线程A先判断是否为0,不为0,那么此时进行加1操作,同一时刻的B线程此时判断的是number等于0,那么就进行等待操作,这个时候A线程加完了,然后通过notifyAll()方法来唤醒其他的线程,所以就完成了减的操作,这里for0~10是为了保证能够交替进行10次。
wait 方法和notify方法是Thread的方法吗?
这里扩展一个知识点,wait 方法和notify方法是Thread的方法吗?
答案:错错错 。
查看API我们可以见到这两个方法属于Object的方法,wait 和 notify 必须要配合synchronized 关键字使用。
这就完了吗??
需求变更
不不不,此时需求改动了!这个时候项目经理过来说,小王,我不要两个线程操作了,我要四个线程同时操作,两个进行相加,两个进行相减,还是交替到时必须是0,1相互的交替。
这个时候心想,简单呀,我再多加两个线程!
于是,多加了两个线程。
代码如下,Resource不变。
public class ThreadWaitNotifyDemo { public static void main(String[] args) { Resource resource = new Resource(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { resource.up(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { resource.down(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "B").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { resource.up(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "C").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { resource.down(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "D").start(); } }
和之前的一样,多加了两个线程,一个进行加,一个进行减操作。
然后run一下,结果如下图:
这里我们就看到,有的为3了,这是什么情况???
分析:举个例子,A,C线程是进行加操作,B,D是进行减操作,此时如果是0,那么B,和D线程是被wait了的对吧,A和C因为up方法被加了锁,所以只有一个方法进行加,如果此时A进行完操作,然后再notifyall,那么此时C线程也会进行up操作,
因为C线程在if(number!=0){this.wait();}这里被唤醒后,继续进行其他的操作了,且在A执行完加1操作和后没有继续判断number的情况,同理B,D线程也是如此,所以就会出现上图的情况!
那么这种情况怎么解决呢??
解决方案
因为我们是没有重新进行判断,那么,我们让其重新进行判断就是了!
修改资源类代码如下:
class Resource { private int number = 0; public synchronized void up() throws InterruptedException { //1.判断 while (number != 0) { this.wait(); } //2.干活 number++; System.out.println(Thread.currentThread().getName() + "\t" + number); this.notifyAll(); } public synchronized void down() throws InterruptedException { while (number == 0) { this.wait(); } number--; System.out.println(Thread.currentThread().getName() + "\t" + number); this.notifyAll(); } }
四个线程的操作资源的代码不变,结果如下:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)