wait()和notify()必须在synchronized块中调用,且需用while循环而非if检查条件以应对虚假唤醒;多线程场景优先用notifyall();推荐使用lock+condition替代原生机制。

wait() 和 notify() 必须在 synchronized 块里调用
直接调用 wait() 或 notify() 会抛出 IllegalMonitorStateException,因为这两个方法依赖对象的内置锁(monitor)。JVM 要求当前线程必须已持有该对象的锁,才能操作其等待队列。
- 错误写法:
obj.wait();(没加synchronized) - 正确写法:
synchronized (obj) { obj.wait(); } - 注意:用哪个对象调用
wait(),就要对哪个对象加锁;notify()同理 - 如果锁的是
this,但调用的是otherObj.wait(),依然报错
为什么必须用 while 而不是 if 检查条件
虚假唤醒(spurious wakeup)是真实存在的——即使没人调用 notify(),wait() 也可能提前返回。只用 if 判断会导致线程在条件不满足时继续执行,引发逻辑错误。
- 典型场景:生产者-消费者中,消费者发现
queue.isEmpty()为 true 后进入 wait;被唤醒后必须重新检查是否真有元素可取 - 正确模式:
synchronized (lock) { while (!conditionMet()) { lock.wait(); } // 安全执行后续操作 } - Java 文档明确建议使用
while循环,而非if
notify() 和 notifyAll() 的选择直接影响线程安全性
单个等待线程时,notify() 足够;但多数实际场景(如多个消费者等待同一队列)中,用 notify() 极易导致死锁或饥饿。
-
notify()只唤醒一个随机等待线程,若它发现条件仍不满足又回到 wait,其他线程可能永远等不到通知 -
notifyAll()唤醒所有等待线程,让它们重新竞争锁并各自判断条件——这是更安全、更可预测的做法 - 性能差异在现代 JVM 中已不明显,除非高并发且严格压测,否则优先选
notifyAll() - 不要为了“省一点唤醒开销”而冒险用
notify()替代notifyAll()
推荐用 Lock + Condition 替代原生 wait/notify
原生机制难调试、易出错,java.util.concurrent.locks.Condition 提供了语义更清晰、功能更可控的替代方案。
立即学习“Java免费学习笔记(深入)”;
- 一个
Lock可绑定多个Condition,比如notFull和notEmpty,避免不同等待逻辑互相干扰 - 方法名更直观:
await()/signal()/signalAll() - 支持带超时的等待:
await(5, TimeUnit.SECONDS),避免无限阻塞 - 示例:
Lock lock = new ReentrantLock(); Condition notEmpty = lock.newCondition(); lock.lock(); try { while (queue.isEmpty()) { notEmpty.await(); } return queue.poll(); } finally { lock.unlock(); }










