wait与notify是基于对象监视器的协作协议,必须在synchronized块中成对使用;wait释放锁并进入等待队列,notify仅标记唤醒但不释放锁;须用while循环校验条件防虚假唤醒;notify适用于一对一场景,notifyAll更安全通用。

Java 中的 wait 与 notify 不是独立运行的“指令”,而是一套基于对象监视器(Monitor)的协作协议。它们必须成对出现、共用同一把锁、围绕明确条件展开,否则极易导致死锁、假唤醒或 IllegalMonitorStateException。
必须在 synchronized 块中调用
wait 和 notify 都是 Object 类的 final 方法,调用前线程必须已持有目标对象的锁。脱离同步上下文调用会直接抛出 IllegalMonitorStateException。
- 正确写法:先用
synchronized(obj)获取锁,再在块内调用obj.wait()或obj.notify() - 错误写法:在普通方法体或未加锁代码中直接调用 —— JVM 检测到无锁状态即报错
- 关键细节:notify() 执行后不会立刻释放锁;只有退出整个 synchronized 块时,锁才真正释放,此时等待线程才有机会竞争锁
wait 会释放锁,notify 不会立即释放锁
这是理解协作流程的核心差异:
-
wait()一执行,当前线程立刻释放锁,并进入该对象的等待队列(Wait Set),暂停执行 -
notify()只是“发信号”:从等待队列中选一个线程标记为“可唤醒”,但被唤醒线程仍需等通知方退出 synchronized 块、释放锁后,才能重新争锁 - 被唤醒线程抢到锁后,才从
wait()调用处继续向下执行 —— 不是跳转到 notify 后面的代码
永远用 while 循环检查条件,不用 if
避免“虚假唤醒”(spurious wakeup)导致逻辑错误。JVM 或操作系统可能在无通知情况下唤醒等待线程。
立即学习“Java免费学习笔记(深入)”;
- 错误示范:
if (queue.isEmpty()) { obj.wait(); }→ 唤醒后直接消费,可能空指针 - 正确写法:
while (queue.isEmpty()) { obj.wait(); }→ 唤醒后再次校验,不满足则继续等 - 生产者-消费者场景中,notify 后条件未必成立(如多个消费者竞争),循环校验是强制安全实践
notify 与 notifyAll 的选择逻辑
二者语义不同,适用场景明确:
-
notify():适合“一对一”精确唤醒,例如一个生产者 + 一个消费者,且每次只产生/消耗一项资源 -
notifyAll():更常用、更安全。当存在多个等待线程或条件复杂(如多个消费者、多类等待条件)时,避免因随机唤醒遗漏关键线程 - 注意:notifyAll 并不等于“全部立即执行”,所有被唤醒线程仍要排队抢锁,实际仍是串行进入临界区


















