一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情。
小故事 - 为什么需要 wait
-
由于条件不满足,小南不能继续进行计算
-
但小南如果一直占用着锁,其它人就得一直阻塞,效率太低
-
于是老王单开了一间休息室(调用 wait 方法),让小南到休息室(WaitSet)等着去了,但这时锁释放开,
-
其它人可以由老王随机安排进屋
-
直到小M将烟送来,大叫一声 [ 你的烟到了 ] (调用 notify 方法)
- 小南于是可以离开休息室,重新进入竞争锁的队列
原理
-
Owner 线程发现条件不满足,调用 wait 方法,即可进入 WaitSet 变为 WAITING 状态
扫描二维码关注公众号,回复: 13779386 查看本文章 -
BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用 CPU 时间片
-
BLOCKED 线程会在 Owner 线程释放锁时唤醒
-
WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入EntryList 重新竞争
API
-
obj.wait()
让进入 object 监视器的线程到 waitSet 等待 -
obj.notify()
在 object 上正在 waitSet 等待的线程中挑一个唤醒 -
obj.notifyAll()
让 object 上正在 waitSet 等待的线程全部唤醒
它们都是线程之间进行协作的手段,都属于 Object 对象的方法。必须获得此对象的锁,才能调用这几个方法
-
wait() 方法会释放对象的锁,进入 WaitSet 等待区,从而让其他线程就机会获取对象的锁。无限制等待,直到notify 为止
-
wait(long n) 有时限的等待, 到 n 毫秒后结束等待,或是被 notify
wait notify 的正确使用方式
sleep(long n) 和 wait(long n) 的区别
- sleep 是 Thread 方法,而 wait 是 Object 的方法
- sleep 不需要强制和 synchronized 配合使用,但 wait 需要和 synchronized 一起用
- sleep 在睡眠的同时,不会释放对象锁的,但 wait 在等待的时候会释放对象锁
- 它们状态 TIMED_WAITING
代码模拟
step1
-
其它干活的线程,都要一直阻塞,效率太低
-
小南线程必须睡足 2s 后才能醒来,就算烟提前送到,也无法立刻醒来
-
加了 synchronized (room) 后,就好比小南在里面反锁了门睡觉,烟根本没法送进门,main 没加synchronized 就好像 main 线程是翻窗户进来的
-
解决方法,使用 wait - notify 机制
step2
-
解决了其它干活的线程阻塞的问题
-
但如果有其它线程也在等待条件呢?
step3
-
notify 只能随机唤醒一个 WaitSet 中的线程,这时如果有其它线程也在等待,那么就可能唤醒不了正确的线程,称之为【虚假唤醒】
-
解决方法,改为 notifyAll
step4
-
用 notifyAll 仅解决某个线程的唤醒问题,但使用 if + wait 判断仅有一次机会,一旦条件不成立,就没有重新判断的机会了
-
解决方法,用 while + wait,当条件不成立,再次 wait