正确实现需守住三条底线:锁对象统一(必须为共享缓冲区实例)、条件循环校验(wait须置于while中防虚假唤醒)、全员唤醒机制(必须用notifyall而非notify);还需妥善处理interruptedexception。

要用 Object 的 wait() 和 notifyAll() 正确实现生产者消费者模型,关键不是“完美重构”,而是守住三条底线:锁对象统一、条件循环校验、全员唤醒机制。notify() 单点唤醒在真实调度中不可靠,必须用 notifyAll()。
锁对象必须是共享缓冲区本身
生产者和消费者要看到彼此的通知,就必须竞争同一把锁。常见错误是用 this、新 new Object() 或不同引用作锁,导致 wait/notify 彼此失联。
- 正确做法:所有同步块都以
buffer(比如ArrayList或LinkedList实例)为锁对象,例如synchronized(buffer) { ... } - 不能写成
synchronized(this)或另建一个private final Object lock = new Object()—— 否则生产者唤醒的是自己那一组线程,消费者永远收不到信号 - 锁对象一旦选定,读、写、wait、notifyAll 必须全部作用于它,不能混用
wait() 必须套在 while 循环里
虚假唤醒(spurious wakeup)是 JVM 允许的行为:线程可能没被 notify 就自行醒来。if 判断会直接跳过条件检查,导致取空队列或写满缓冲区。
- 生产者等待逻辑:
while (buffer.size() >= capacity) buffer.wait(); - 消费者等待逻辑:
while (buffer.isEmpty()) buffer.wait(); - 每次 wait 返回后,线程重新获得锁,必须再次检查条件是否成立,不成立就继续 wait
一律用 notifyAll(),别碰 notify()
notify() 只随机唤醒一个等待线程,但无法区分角色。生产者调用 notify(),可能只唤醒另一个生产者;消费者调用 notify(),也可能只唤醒另一个消费者——另一方持续挂起,系统死锁。
- notifyAll() 不是让所有线程立刻执行,而是把它们全移出等待队列,重新参与锁竞争
- 每个被唤醒的线程进入同步块后,仍由自己的 while 条件决定是否真能干活,逻辑自洽且鲁棒
- 即使单生产者+单消费者,notifyAll() 也更安全,避免因线程调度顺序导致的隐性阻塞
别忘了中断响应和异常处理
wait() 声明抛出 InterruptedException,这不是可选提醒,而是线程协作的生命线。
- 不能捕获后空 catch 或仅打日志——这等于屏蔽外部终止指令
- 推荐做法:向上声明 throws,或在 catch 中调用
Thread.currentThread().interrupt()恢复中断状态 - 否则,当主线程调用
thread.interrupt()试图优雅退出时,等待中的线程将无响应











