notifyall() 不解决生产者消费者问题,而是需在 synchronized 块中调用,配合 while 循环条件检查和封装化设计,确保多线程下状态变更的公平唤醒与安全执行。

在 Java 中,notifyAll() 本身不“解决”生产者消费者问题,而是配合 wait() 和同步机制,让多个等待线程公平、安全地响应状态变化。关键在于:必须在同步块中调用,且唤醒后需重新检查条件(用 while 而非 if),避免虚假唤醒和竞态。
必须在 synchronized 块中调用 notifyAll
notifyAll() 只能在当前线程持有对象锁时调用,否则抛出 IllegalMonitorStateException。生产者在放入数据后、消费者在取走数据后,都要在共享资源(如缓冲区)的锁保护下通知所有等待方:
- 生产者存入一个元素后,调用
buffer.notifyAll(),告诉可能等待“有空位”的消费者,也告诉可能等待“有数据”的消费者——但后者会因条件不满足继续 wait - 消费者取走一个元素后,同样调用
buffer.notifyAll(),唤醒可能等待“有空位”的生产者,也唤醒其他消费者(它们会再次判断是否有数据可取)
用 while 循环检查条件,而非 if
被 notifyAll() 唤醒的线程不一定满足执行条件(比如多个消费者被唤醒,但只有一个数据)。若用 if 判断,可能跳过检查直接消费,导致 IndexOutOfBoundsException 或逻辑错误:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 消费者应写成:
while (buffer.isEmpty()) { buffer.wait(); } - 生产者应写成:
while (buffer.isFull()) { buffer.wait(); } - 每次从 wait 返回,都重新判断条件是否真正满足,不满足就继续 wait
共享缓冲区需封装 wait/notifyAll 逻辑
把同步、等待、通知封装在缓冲区类内部,能避免外部误操作。例如:
class BoundedBuffer {
private final List
private final int capacity;
public BoundedBuffer(int capacity) { this.capacity = capacity; }
public synchronized void put(int item) throws InterruptedException {
while (list.size() == capacity) wait();
list.add(item);
notifyAll(); // 唤醒所有等待者
}
public synchronized int take() throws InterruptedException {
while (list.isEmpty()) wait();
int item = list.remove(0);
notifyAll(); // 同样唤醒所有
return item;
}
}
notifyAll 比 notify 更安全,尤其多生产者多消费者场景
用 notify() 可能唤醒错误类型的线程(如唤醒另一个消费者,而此时其实该唤醒生产者),导致所有线程长期阻塞。而 notifyAll() 让所有等待线程竞争锁并重新判断条件,系统自动筛选出真正能工作的线程:
- 即使唤醒了暂时不能工作的线程,它也会立刻再次 wait,不浪费资源
- 避免因唤醒遗漏导致的死锁或饥饿
- 虽然开销略大,但在大多数业务场景下可接受,且更健壮
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










