wait方法必须在持有对象锁的前提下调用,否则抛IllegalMonitorStateException;其本质是JVM对Monitor的原子操作,包含释放锁和入WaitSet两步,且条件检查与wait需在synchronized块中完成以避免竞态与虚假唤醒。

因为 wait 方法本质是 JVM 对对象监视器(Monitor)的一次原子操作,必须由锁的持有者执行,否则无法安全释放锁并挂起线程。
wait 的底层动作是“释放锁 + 入队等待”
调用 wait() 时,JVM 实际执行两个不可分割的动作:
- 把当前线程从该对象的 monitor owner 字段中移出
- 将线程放入该对象的 WaitSet(等待队列)
这两个动作必须在持有 monitor 锁的前提下完成。JVM 的 wait 指令会先检查 Thread.holdsLock(obj),不满足就直接抛 IllegalMonitorStateException。这不是 Java 语言层的约定,而是 JVM 指令(如 monitorenter / wait)的硬性契约。
没有锁就没有 wait 的上下文
每个 Java 对象内部都关联一个 Monitor 结构,包含 owner、EntryList 和 WaitSet 三个核心字段。只有 owner 线程才有权限修改自身状态——比如把自己从 owner 清除、再塞进 WaitSet。如果线程没拿到锁就调用 wait(),JVM 根本不知道该操作哪个对象的 WaitSet,也无法保证线程状态与锁状态一致。
立即学习“Java免费学习笔记(深入)”;
- 非 owner 线程调用 wait() → JVM 无法定位合法的 WaitSet → 拒绝执行
- notify()/notifyAll() 同理:必须是 owner 才能从 WaitSet 中取出线程并放入 EntryList
同步块保障条件检查与等待的原子性
实际使用中,wait 几乎总和条件判断配合,例如:
synchronized (obj) {
while (!condition) {
obj.wait();
}
}
这个结构的关键在于:检查 condition 和执行 wait() 必须被同一把锁保护。否则会出现竞态:
- 线程 A 判断 condition 为 false,准备 wait
- 线程 B 此时把 condition 设为 true 并调用 notify()
- 线程 A 还没 wait 就错过了信号(Missed Signal)
而 synchronized 块确保了“判断 → 等待”是一气呵成的原子过程,同时保证唤醒后重新持锁,能立即重检条件,避免虚假唤醒问题。
类比理解:进屋才能登记等通知
可以把对象 monitor 看作一间带门禁的屋子:
- synchronized 是进门刷卡动作,成功后成为“屋主”(owner)
- wait 是向管理员提交“等通知”申请,并交出钥匙(释放锁)
- 没进门(没持锁)就递申请?管理员根本不认——直接拒收
notify 同样如此:只有屋主才能叫人进来,外人喊破喉咙也没用。


















