notify() 未唤醒等待线程的根本原因是它只唤醒当前在 wait set 中的线程,若 notify 先于 wait 执行,通知即丢失;必须用 while 循环检查条件变量,且确保 wait/notify 在 synchronized 块内调用。

为什么 notify() 没唤醒等待线程?
根本原因不是 notify() 失效,而是它只唤醒「当前在 wait set 里」的线程;如果唤醒发生在 wait() 之前(即先 notify 后 wait),信号就彻底丢失——JVM 不缓存通知,也不排队。
常见错误现象:wait() 一直不返回,线程卡死在 Object.wait(),jstack 显示 WAITING (on object monitor) 状态。
- 必须在
synchronized块内调用wait()和notify(),否则抛IllegalMonitorStateException - 唤醒逻辑不能依赖「某次 notify 必定对应某次 wait」,要始终用 while 循环检查条件变量,而不是 if
- 多个等待者共用同一把锁时,
notify()可能唤醒错人,优先用notifyAll(),除非你明确控制了等待者数量且顺序可预测
条件变量没用 while 包裹,直接导致信号丢失
用 if 判断条件后 wait(),等于默认“通知一定准时”,但并发下条件可能被其他线程抢改,或通知提前到达。一旦错过,线程就永远等下去。
正确写法必须是:
synchronized (lock) {
while (!conditionMet) {
lock.wait();
}
// do work
}
-
conditionMet必须是 volatile 或受同一锁保护的共享变量 - 每次
wait()返回都要重新检查条件,因为可能被虚假唤醒(spurious wakeup)或条件又被其他线程改回 false - 不要在循环体外做条件判断,哪怕你“确定”只等一次
notify() 在 wait() 之前执行,怎么定位?
这种时序问题不会报错,只能靠日志或 jstack + 线程状态交叉比对。典型线索:唤醒方已执行完 notify(),但等待方仍卡在 wait(),且两者共用同一对象锁。
- 加日志:在
notify()前打 “about to notify, condition = true”,在wait()前打 “entering wait, condition = false” - 用 jstack 抓线程快照,确认等待线程是否真的在 wait set 中(状态为 WAITING)、唤醒线程是否已完成 notify 调用
- 避免在异步任务、回调、定时器中随意 notify——它们和主线程 wait 的时序完全不可控
Java 里用 Lock + Condition 更容易踩坑?
是的。Condition.await() 和 signal() 同样不保证通知不丢失,但多了个易错点:同一个 Lock 可绑定多个 Condition,如果 signal 和 await 用了不同的 Condition 实例,那必然收不到。
- 确认
await()和signal()调用的是同一个Condition对象(比如都来自lock.newCondition()的同一返回值) -
signal()不会抛异常,即使没人 await,也不会警告——静默失败 - 比起
Object.wait(),Condition支持中断响应(awaitUninterruptibly()除外),但这也意味着你要处理InterruptedException,别吞掉它









