线程通信wait() notify() notifyAll()的正确使用

本文主要是文字描述,如果有耐心则坚持看完,理解了必有收获~~

 

基于使用 synchronized 锁,JVM都会为锁对象维护两个集合,你必须知道的两个概念:

   Entry Set(别名锁池):线程获取对象锁失败,则会进入这个对象的锁池。

      (若线程A获取了对象锁,那么线程B再来获取这个对象锁,必定失败,B线程则会进入锁池)

   Wait Set(别名等待池):线程获取锁后,调用了wait方法,那么就会进入这个对象的等待池。

    (若线程A获取了对象锁,那么线程A调用了wait方法,A会释放锁,然后进入等待池)

 

再来看这三个方法,这三个方法必须放在synchronized代码块或者方法块中使用,否则会抛出:java.lang.IllegalMonitorStateException 异常

   wait:调用这个方法,线程就会释放锁,然后进入等待池,必须等待别的线程唤醒自己。

             和sleep不一样,1是sleep不会释放锁,2是sleep会在睡眠指定时间后被唤醒。

   notify:调用这个方法,则会环境等待池中的一个线程

             Note:被唤醒的线程不会立马执行,它只是从blocked -> runnable状态,形象的说是这个

                         被唤醒的线程从等待池中进入到锁池,锁池中的线程会去竞争该对象锁,获取到了才能执行

   notifyAll:唤醒等待池中的所有线程,这是和 notify 唯一的区别

 

再来看两个常被提到的问题:

   1 wait 要放在while里面循环,不要放在if语句中,否则不安全,可能出现死锁。

   2 唤醒最好调用notifyAll,不要使用notify,否者不安全,可能出现死锁。

 

这两个说法并没有错,但是前提是多生产者和多消费者的情况下才是完全正确,但是如是一个生产者线程和一个消费者线程,那么问题1中也是可以使用if语句的,2中也是可以使用notify。

 

多消费者-多生产者下为什么不要在if语句里面使用wait()?

如果只有一个生产者线程,一个消费者线程,那其实是可以用if代替while的,因为线程调度的行为是开发者可以预测的,生产者线程只有可能被消费者线程唤醒,反之亦然,因此被唤醒时条件始终满足,程序不会出错。

但是在多消费者-多生产者下,wait()的线程永远不能确定其他线程会在什么状态下notify(),所以必须在被唤醒、抢占到锁并且从wait()方法退出的时候再次进行指定条件的判断,以决定是满足条件往下执行呢还是不满足条件再次wait()

 

 

多消费者-多生产者下为什么要用notifyAll?

两个生产者两个消费者的场景,如果我们代码中使用了notify()而非notifyAll(),假设消费者线程1拿到了锁,判断资源为空,那么wait(),释放锁;然后消费者2拿到了锁,同样资源为空,wait(),也就是说此时Wait Set中有两个线程;然后生产者1拿到锁,生产,有资源了,notify()了,那么可能消费者1被唤醒了,但是此时还有另一个线程生产者2在Entry Set中盼望着锁,并且最终抢占到了锁,但因为此时资源是有的,因此它要wait();然后消费者1拿到了锁,消费,notify();这时就有问题了,此时生产者2和消费者2都在Wait Set中,资源为空,如果唤醒生产者2,没毛病;但如果唤醒了消费者2,因为资源为空,它会再次wait(),这就尴尬了,万一生产者1已经退出不再生产了,没有其他线程在竞争锁了,只有生产者2和消费者2在Wait Set中互相等待,那传说中的死锁就发生了。

 

最后贴一个图看看demo吧

这是一个生产者和一个消费者,如果是多个生产设和多个消费者,直接将 notify() 改为 notifyAll() 即可

补充:

   对于notify的问题,如果使用AQS里面的ReentrantLock来解决

ReentrantLock reentrantLock = new ReentrantLock();
Condition condition1 = reentrantLock.newCondition();
Condition condition2 = reentrantLock.newCondition();

   这个时候就可以让生产者使用 condition1,消费者使用 condition2 来解决这个问题

Condition 类其实可以理解Object.wait 和 Object.notify 方法的封装类,只不过同一个ReentrantLock下的Condition是共享一个锁

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值