《重新学习多线程》-- wait notify

511 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情

小故事 - 为什么需要 wait

  • 由于条件不满足,小南不能继续进行计算

  • 但小南如果一直占用着锁,其它人就得一直阻塞,效率太低

image.png

  • 于是老王单开了一间休息室(调用 wait 方法),让小南到休息室(WaitSet)等着去了,但这时锁释放开,

  • 其它人可以由老王随机安排进屋

  • 直到小M将烟送来,大叫一声 [ 你的烟到了 ] (调用 notify 方法)

image.png

  • 小南于是可以离开休息室,重新进入竞争锁的队列

image.png

原理

image.png

  • Owner 线程发现条件不满足,调用 wait 方法,即可进入 WaitSet 变为 WAITING 状态

  • 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) 的区别

  1. sleep 是 Thread 方法,而 wait 是 Object 的方法
  2. sleep 不需要强制和 synchronized 配合使用,但 wait 需要和 synchronized 一起用
  3. sleep 在睡眠的同时,不会释放对象锁的,但 wait 在等待的时候会释放对象锁
  4. 它们状态 TIMED_WAITING

代码模拟

step1

carbon (29).png

  • 其它干活的线程,都要一直阻塞,效率太低

  • 小南线程必须睡足 2s 后才能醒来,就算烟提前送到,也无法立刻醒来

  • 加了 synchronized (room) 后,就好比小南在里面反锁了门睡觉,烟根本没法送进门,main 没加synchronized 就好像 main 线程是翻窗户进来的

  • 解决方法,使用 wait - notify 机制

step2

carbon (30).png

  • 解决了其它干活的线程阻塞的问题

  • 但如果有其它线程也在等待条件呢?

step3

carbon (31).png

  • notify 只能随机唤醒一个 WaitSet 中的线程,这时如果有其它线程也在等待,那么就可能唤醒不了正确的线程,称之为【虚假唤醒】

  • 解决方法,改为 notifyAll

step4

carbon (32).png

  • 用 notifyAll 仅解决某个线程的唤醒问题,但使用 if + wait 判断仅有一次机会,一旦条件不成立,就没有重新判断的机会了

  • 解决方法,用 while + wait,当条件不成立,再次 wait

step5

carbon (33).png