必须在synchronized块中对同一对象调用wait()和notifyall(),且所有等待线程需共享该对象锁;唤醒后需用while循环检查条件,因存在虚假唤醒。

在 Java 中,wait() 和 notifyAll() 是 Object 类提供的原生方法,用于线程间协作。要通过 notifyAll() 唤醒所有正在同一对象上等待的线程,关键在于:所有线程必须在**同一个对象监视器(monitor)上调用 wait()**,且唤醒方必须在**同一对象上调用 notifyAll()**,并且整个过程需在 synchronized 块中进行。
必须在 synchronized 块中使用 wait/notifyAll
wait() 和 notifyAll() 都是 JVM 级别的监视器操作,只能在持有对象锁的前提下调用,否则会抛出 IllegalMonitorStateException。因此,它们必须出现在 synchronized(obj) { ... } 内部。
- 调用
obj.wait()前,当前线程必须已获得obj的锁(即已进入synchronized(obj)) - 调用
obj.notifyAll()同样要求当前线程持有obj的锁 - 一个常见错误是:在不同对象上同步,比如
synchronized(this)调用otherObj.wait()—— 这会直接报错
所有等待线程需共享同一个锁对象
notifyAll() 只能唤醒那些在**当前对象实例**上调用了 wait() 的线程。如果多个线程分别在不同对象上等待,notifyAll() 对它们无效。
- ✅ 正确示例:所有线程都执行
synchronized(sharedLock) { sharedLock.wait(); } - ❌ 错误示例:线程 A 在
obj1上 wait,线程 B 在obj2上 wait,再对obj1.notifyAll()只能唤醒 A,无法影响 B - 建议显式定义一个专用的锁对象(如
private final Object lock = new Object();),避免混用this或易被外部干扰的对象
典型协作模式:条件检查 + 循环等待
不能假设 notifyAll() 唤醒后条件就一定满足,因为存在虚假唤醒(spurious wakeup)或竞争。标准写法是把 wait() 放在 while 循环中检查条件。
- 等待方:
while (!conditionMet) { obj.wait(); } - 唤醒方:修改条件后,调用
obj.notifyAll() - 例如:生产者-消费者中,消费者等待“队列非空”,生产者添加元素后
notifyAll();但被唤醒的消费者仍需再次检查队列是否真有数据
notifyAll() 不等于立即执行,只是解除阻塞
notifyAll() 的作用是让所有等待线程从 WAITING 状态变为 BLOCKED(重新竞争锁),而非直接恢复运行。它们需重新获取锁后才能继续执行 wait() 后的代码。
- 被唤醒的线程不会立刻执行后续逻辑,而是排队等待进入
synchronized块 - 锁释放时机很重要:通常在
notifyAll()后尽快退出synchronized块,以便等待线程能及时抢到锁 - 不要在
notifyAll()后继续长时间操作,否则会延迟唤醒响应
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











