notifyall()用于唤醒同一对象上所有wait线程,必须在synchronized块中调用,不保证顺序且不释放锁;需与wait配合使用同一对象锁,适用于多对多协作场景以避免虚假唤醒和死锁。

在 Java 多线程中,notifyAll() 用于唤醒在当前对象监视器上等待的所有线程(即调用了 wait() 的线程),但它必须在 synchronized 同步块或方法中调用,否则会抛出 IllegalMonitorStateException。它不保证唤醒顺序,也不释放锁——被唤醒的线程需重新竞争获取锁后才能继续执行。
notifyAll 的基本使用条件
notifyAll() 是 Object 类的方法,只能在持有该对象锁的前提下调用。常见错误是忘记加 synchronized,或在错误的对象上调用(比如对非 wait 所用对象调用)。
- 必须与
wait()配合使用,且两者操作的是同一个对象锁 - 调用前当前线程必须已获得该对象的 monitor 锁
- 被唤醒的线程不会立即执行,而是进入“锁竞争队列”,等待重新获取锁
为什么不用 notify 而用 notifyAll
当多个线程因不同条件等待同一对象时(例如生产者-消费者中多个消费者等待数据、多个生产者等待空间),仅用 notify() 可能唤醒不满足条件的线程,导致虚假唤醒或死锁。而 notifyAll() 让所有等待线程都重新检查条件,更安全可靠。
- 避免因条件变量变化未覆盖到某类等待线程而造成的永久阻塞
- 配合 while 循环检查条件(而非 if),可抵御虚假唤醒
- 尤其适用于“多对多”协作场景,如线程池任务分发、资源池空闲通知等
典型代码结构:生产者-消费者示例
以下是一个使用 notifyAll() 协调生产和消费的简化模型:
synchronized (lock) {
while (buffer.isEmpty()) {
try {
lock.wait(); // 等待有数据
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
// 消费数据
int data = buffer.remove();
System.out.println("Consumed: " + data);
// 通知所有等待线程(可能有生产者在等空间,也可能有消费者在等新数据)
lock.notifyAll();
}
注意:生产者端同样需用 while (buffer.isFull()) lock.wait(); 和 lock.notifyAll();,确保状态变更后所有相关方都能响应。
常见陷阱与注意事项
notifyAll() 不等于“立刻全部运行”,它只是解除等待状态。实际执行仍取决于锁竞争和调度器。滥用可能导致性能下降(大量线程争抢锁),但安全性优先于微优化。
- 永远用 while 而不是 if 检查 wait 条件,防止虚假唤醒或条件已变
- 不要假设唤醒顺序,业务逻辑不应依赖唤醒先后
- 避免在循环中频繁调用
notifyAll()(如无状态变化时),减少不必要的上下文切换 - 若明确只有一个线程需要被唤醒且条件唯一,
notify()更高效;否则默认选notifyAll()
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











