重构生产者消费者模型应使用object的wait()/notifyall()实现条件等待,核心是统一锁对象、用while循环防虚假唤醒、always notifyall()保证鲁棒性,并正确处理中断。

用 Object 的 wait() 和 notifyAll() 重构生产者消费者模型,关键不是替换工具,而是把协作逻辑从“轮询+sleep”或“外部锁控制”转向基于条件等待的自然调度。核心是让线程在不满足执行条件时主动释放锁并挂起,而不是空转消耗资源。
共享缓冲区必须作为同步锁对象
所有生产、消费、等待、唤醒操作,必须围绕同一个锁对象展开。推荐直接用缓冲区实例(如 LinkedList 或自定义 BoundedBuffer)作锁,避免锁对象错位导致通知失效。
- 错误做法:生产者 synchronized(list) 调用 list.wait(),消费者却 synchronized(queue) 调用 queue.notifyAll() —— 锁不同,通知不达
- 正确做法:统一使用
synchronized(buffer),且buffer.wait()和buffer.notifyAll()都在其中 - 若需解耦,可声明私有锁对象:
private final Object lock = new Object();,生产者和消费者都用它同步
必须用 while 循环包裹 wait() 调用
虚假唤醒(spurious wakeup)是 JVM 允许的行为,线程可能在没被显式 notify 的情况下自行醒来。仅用 if 判断会导致状态不一致,比如从空队列取元素抛出异常。
- 生产者等待条件:
while (buffer.size() >= capacity) - 消费者等待条件:
while (buffer.isEmpty()) - 每次 wait() 返回后,必须重新检查条件是否真正满足,再决定继续等待还是执行后续逻辑
统一用 notifyAll() 替代 notify()
notify() 只唤醒一个等待线程,但无法保证唤醒的是对应角色(例如生产者调用 notify() 可能只唤醒另一个生产者,而消费者仍在空等)。notifyAll() 让所有等待线程竞争锁,由各自的 while 条件筛选出真正可执行者,逻辑更鲁棒。
- 即使只有单生产者+单消费者,notifyAll() 也更安全,避免因调度顺序导致的隐性死锁
- notifyAll() 不等于“立刻全部执行”,只是将线程移出等待队列;谁先拿到锁、谁先检查条件,由 JVM 决定
- 性能开销在绝大多数业务场景下可忽略,稳定性优先
注意中断响应与异常处理
wait() 声明抛出 InterruptedException,不能简单捕获后吞掉。应向上抛出或恢复中断状态,确保线程能响应外部终止信号。
- 在 catch 块中调用
Thread.currentThread().interrupt();是常见做法 - 不要在同步块内做耗时操作(如 I/O、远程调用),否则会阻塞其他线程获取锁
- 共享变量读写必须严格限定在同步块内,禁止在 synchronized 外直接访问
buffer.size()等状态











