Wait、notify和notifyAll以及多线程中经常使用的保留字在实际开发中往往不被重视,本文介绍了这些关键字的使用。
存在即合理
在Java中,每个对象都有两个池,锁池(monitor)和等待池(waitset),每个对象又都有wait、notify、notifyAll方法,使用它们可以实现线程之间的通信,只是平时用的较少。
wait(): 使当前线程处于等待状态,直到另外的线程调用notify或notifyAll将它唤醒
notify(): 唤醒该对象监听的其中一个线程(规则取决于JVM厂商,FILO,FIFO,随机…)
notifyAll(): 唤醒该对象监听的所有线程
锁池: 假设T1线程已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用该对象的synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前都需要先获得该对象的锁的拥有权,但是该对象的锁目前正被T1线程拥有,所以这些线程就进入了该对象的锁池中。
等待池: 假设T1线程调用了某个对象的wait()方法,T1线程就会释放该对象的锁(因为wait()方法必须出现在synchronized中,这样自然在执行wait()方法之前T1线程就已经拥有了该对象的锁),同时T1线程进入到了该对象的等待池中。如果有其它线程调用了相同对象的notifyAll()方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,从新争夺锁的拥有权。如果另外的一个线程调用了相同对象的notify()方法,那么仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池.
注意事项
在调用wait(), notify()或notifyAll()的时候,都必须获得某个对象(注意:不是类)的锁。
永远在循环(loop)里调用 wait 和 notify,而不是在 If 语句中
永远在synchronized的函数或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。
使用案例 – 生产消费
分析: 从日志中可以看到数据会出现多次生产或多次消费的问题,因为在线程执行过程中,两个线程缺少协作关系,都是各干各的,T1线程只管生产数据,不管T2线程是否处理了。
改进方案 – wait/notify
上文已经介绍了使用wait和notify的前提了,接下来看案例
分析: 一切都是那么完美,在T1线程中,调用LOCK.wait()将当前线程移入等待池中,并交出执行权,锁池中的其他线程去竞争并取得锁的使用权(T2线程获取),当T2线程消费完毕后,调用LOCK.notify()方法通知当前对象锁等待池中的其中一个线程(因为这里notify是基于JVM算法而定,因为我们只有两个线程,所以T1线程会接收到T2线程发出的通知,从而继续生产数据。
问题: 虽然一对一没有问题,但假设多个生产者多个消费者的情况下怎么办呢?
BUG – 埋点
分析: 居然不执行了,借助前面说过的 死锁分析知识,我们看看是不是发生死锁了
结果表明,虽然没有Found one deadlock…字眼,但是可以看到有个线程都被wait住了,没有被释放,所以导致我们当前无法继续生产消费
解决方案 – notifyAll
分析: 这里只修改了一句代码,就是将consumer方法中的notify -> notifyAll,由通知单个线程变成通知所有在等待池中的线程
Java程序员学习交流群515675832,既有技术大佬,又有老司机开车,各位对Java感兴趣的可以来交流学习一下,快乐与技术一起进步
1.文章《java 如何实现线程间的通讯》援引自互联网,为网友投稿收集整理,仅供学习和研究使用,内容仅代表作者本人观点,与本网站无关,侵删请点击页脚联系方式。
2.文章《java 如何实现线程间的通讯》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
相关推荐
- . 现代买票为什么带上携程保险
- . 潮阳怎么去广州南站
- . 湖南马拉河怎么样
- . 烧纸为什么到三岔路口
- . 百色为什么这么热
- . 神州租车怎么样
- . 芜湖方特哪个适合儿童
- . 护肤品保养液是什么类目
- . 早晚的护肤保养有哪些项目
- . 女孩护肤品怎么保养的最好