wait()必须在synchronized块中调用,否则抛illegalmonitorstateexception;它原子性地释放锁并入waitset,防止丢失唤醒、保证条件可见性与重检,且jvm强制校验线程是否为锁持有者。

因为 wait() 不是普通暂停方法,而是线程协作原语,必须在持有对象锁的前提下才能安全释放锁并进入等待队列;否则 JVM 无法确认操作上下文,会直接抛出 IllegalMonitorStateException。
本质是“释放锁 + 入等待队列”的原子操作
wait() 并非只让线程停住,它实际完成两个不可分割的动作:
- 释放当前线程持有的该对象的 monitor 锁(即 synchronized 锁)
- 将当前线程挂起,并加入该对象的
WaitSet(等待队列)
这两个动作必须由同一个锁保障一致性。JVM 要求调用线程必须是该对象 monitor 的 _owner(持有者),否则既不知道该释放哪个锁,也无法安全修改 WaitSet 链表结构。
防止丢失唤醒(Lost Wakeup)
这是最关键的业务逻辑原因。若条件检查与 wait() 不在同一个同步块中,就会出现竞态窗口:
- 消费者判断
queue.isEmpty() == true - 此时被调度切换,生产者获取锁、入队、调用
notify() - 但此时
WaitSet为空,通知丢失 - 消费者切回后才执行
wait(),永远等不到唤醒
同步块把「检查条件 → 决定是否等待」打包成原子操作,确保 notify() 不会发生在 wait() 之前。
保证共享状态的可见性与重检条件
wait() 几乎总是配合某个布尔条件使用(如 isReady == false):
- 只有在 synchronized 块中读写该条件,才能借助锁的 happens-before 规则,确保变量修改对其他线程立即可见
- 从
wait()返回时,线程重新持有锁,可立即再次检查条件——这也是为什么必须用while而非if - 虚假唤醒或多个线程竞争时,仅靠一次
if判断无法保证条件仍成立
JVM 层面的硬性契约
HotSpot 中每个对象关联一个 ObjectMonitor 结构,其中包含:
-
_owner:记录当前持锁线程 -
_WaitSet:存放已调用wait()的线程 -
_EntryList:等待获取锁的线程
调用 wait() 时,JVM 会校验 _owner == current_thread;不满足就拒绝执行,直接抛异常。这不是语法建议,而是 monitor 模型的底层约束——就像没开门就不能往保险箱放东西,synchronized 是开门动作,wait() 是放东西进等待区的动作。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











