理解Java中wait/notify

前言

wait/notify作为线程间通信必不可或缺的一种方式,总给我一种非常神秘的感觉,工作中很少用到这两个方法,但是作为Object对象的两个基础方法,我认为还是很有必要去深入理解的。由于这两个方法都是native方法,无法通过查看源码去理解,这里我主要是通过查看国内外一些文章、博客总结出来的,后面我会一一贴出参考地址。

notify方法必须放在同步代码块结尾吗?

并不需要,notify只是唤醒其它处于wait状态的线程重新进入竞争锁状态,notify线程本身并不会立即释放线程,释放时机仍然需要等待当前线程任务执行结束。
下面是JavaDoc中对notify方法的解释:

The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object.

被 唤 醒 的 线 程 并 不 会 立 即 执 行 而 是 要 等 待 当 前 线 程 释 放 对 象 锁 , 唤 醒 的 线 程 会 跟 其 它 正 常 线 程 一 样 去 竞 争 同 步 锁 \color{red}{被唤醒的线程并不会立即执行而是要等待当前线程释放对象锁,唤醒的线程会跟其它正常线程一样去竞争同步锁} 线线线线
所以notify放在代码块中间提前唤醒其它等待线程也没什么关系,不会造成线程不安全问题。伪代码如下:

synchronized(lock) {
    lock.notify();
    doAnother();  //继续执行当前方法
}

这里举个吃饭的例子,餐厅房间只能容纳一个人,后面吃饭的得一个一个来,前一个吃完了,后面才能进入。假设吃饭前大家都在睡觉😄,突然有人喊:“睡着的赶紧醒醒,马上就要吃饭了,一个一个来,等前一个吃完了就从你们里面抽一个进去用餐”。虽然话喊完了,但是后面排队的人并不能进去,前面还在里面吃饭的人不会因为餐厅人喊了一句就直接放弃把剩下吃完。

notify调用对象不是当前同步锁对象?

synchronized(lock) {
    ...
    signal.notify();
}

上面实例中notify所处的同步代码块对应的对象锁是lock对象,但是notify调用对象是signal,如果有尝试同学会发现程序会报IllegalMonitorStateException异常,这也符合JDK规范。
所以看notify中有定义如下这段:

Only one thread at a time can own an object’s monitor.
@throws IllegalMonitorStateException if the current thread is not the owner of this object’s monitor.

上面意思是同一时间,一个线程只能获取一个对象锁,如果当前线程并未持有notify调用对象的对象锁,那将抛出上面非法锁状态异常。

notify VS notifyAll

记得之前出去面试的时候有个人问过我这个问题,当然这种问题大部分人从表面字面量就可以看出他们的区别:前者唤醒一个线程,后者唤醒所有线程。这么解释我认为也是没什么问题,JDK官方文档也是这么说的:如果当前有多个线程在一个队列中等待获取锁,调用notify实现机制就是从这个队列中随机唤醒一个线程,等当前线程释放锁后它即可获取到锁,notifyAll就是唤醒这个队列中所有线程,但是注意:哪怕唤醒了所有线程,但是最终也只有一个能幸运的获得锁并进入同步方法
那到底是用notify还是notifyAll呢?这个要看个人喜好了,个人觉得notifyAll会更好一些,因为一旦多线程情况下如果调用notify后,某个wait线程本应该接收到通知,但是miss了,那这种问题就极难调试了。但是调用notifyAll,它会唤醒所有wait的线程,哪怕其中一两个线程错过了通知,仍会有其它线程能正常接收到通知,幸运的那个会获取到锁并执行同步方法。总而言之,notifyAll跟保险一些吧!
有人说调用notifyAll会消耗更多CPU资源,导致更多的CPU空转。我认为这个也是相对的,因为我们知道,wait方法大部分都是在一个while循环中,如下:

while(sign) {
    lock.wait();
}

我们即使不唤醒这个线程,它仍然会不定期去获取while条件判断的状态值,不断的while循环本就是一种CPU资源浪费,这里我们唤醒他们去竞争一个锁,在性能上并无太大压力。

参考demo

废话说了那么多,还没有上代码,这里我把代码传到Github上,感兴趣的可以down下来看一下,只有一个文件,路径如下:WaitAndSleep.java

参考链接:

  1. Difference between notify and notifyAll in Java - Thread
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值