wait()和notify()依赖synchronized提供的监视器机制,必须在同步块中调用,否则抛illegalmonitorstateexception;wait()释放锁并进入等待队列,notify()随机唤醒一个等待线程,notifyall()唤醒全部,需用while循环防虚假唤醒。

Java 中 Object 类的 wait() 和 notify() 方法是线程间协作的底层基石,但它们本身不直接实现同步——而是依赖 synchronized 块提供的监视器(monitor)机制协同工作。真正起作用的是 JVM 对每个对象关联的监视器锁和等待队列。
wait() 与 notify() 必须在 synchronized 代码块中调用
这两个方法操作的是对象的监视器(monitor),而只有获得该对象锁的线程才能进入其 monitor。若未持有锁就调用,会抛出 IllegalMonitorStateException。
-
wait()会让当前线程释放锁、进入该对象的等待队列,并挂起;直到被其他线程调用notify()或notifyAll()唤醒,或超时/被中断才可能重新竞争锁 -
notify()从该对象的等待队列中随机唤醒一个线程(不保证顺序),被唤醒线程不会立即执行,而是重新参与锁竞争 -
notifyAll()唤醒所有在该对象上等待的线程,适用于多个消费者/生产者场景,避免虚假唤醒遗漏
典型协作模式:生产者-消费者模型
以共享缓冲区为例,体现 wait/notify 如何配合 synchronized 实现状态驱动的协作:
- 生产者在缓冲区满时调用
buffer.wait(),释放锁并等待;消费者消费后调用buffer.notify()唤醒一个生产者 - 消费者在缓冲区空时调用
buffer.wait();生产者插入后调用buffer.notify()唤醒一个消费者 - 必须用 while 循环 检查条件(而非 if),防止虚假唤醒(spurious wakeup)导致逻辑错误
底层机制依赖 JVM 的 ObjectMonitor
JVM 为每个 Java 对象维护一个 ObjectMonitor 结构,包含:owner(持有锁的线程)、EntryList(锁竞争队列)、WaitSet(调用 wait 后挂起的线程队列)。
- 进入
synchronized时,线程尝试获取 monitor;失败则进入 EntryList 等待 - 调用
wait()时,线程从 owner 释放,加入 WaitSet,并让出锁,自身挂起 - 调用
notify()时,JVM 将 WaitSet 中一个线程移入 EntryList,使其有机会重新竞争锁
注意事项与常见误区
这些方法易用错,关键细节决定是否真正协作成功:
- 永远不要对常量字符串或全局共享对象(如
"lock")调用 wait/notify,因类加载器可能共享同一实例,引发意外唤醒 - 避免在循环中无条件调用
notify(),应只在状态真正改变且有等待者时才通知,否则浪费调度 -
wait(long)和wait(long, int)支持超时,但返回后仍需再次检查条件,不能假设“时间到就一定可执行” - 优先使用
notifyAll(),除非能严格保证等待线程行为完全一致且无竞争风险
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











