illegalmonitorstateexception 是 jvm 强制检查,要求 wait()/notify() 必须在持有对象锁的 synchronized 块中调用;objectmonitor 维护 _entrylist 和 _waitset 两队列;notify() 仅操作当前锁对象的 _waitset,且需配合 while 循环重检条件。

IllegalMonitorStateException 是 JVM 的硬性拦截,不是建议而是强制
调用 wait() 或 notify() 时若当前线程未持有目标对象的 monitor 锁,JVM 会立即抛出 IllegalMonitorStateException。这不是运行时偶然问题,而是 HotSpot 在字节码校验阶段就插入的检查逻辑——monitorenter 指令未执行成功,wait 就根本不会进入 ObjectMonitor 的等待队列处理流程。
ObjectMonitor 内部有两套独立队列:同步队列和等待队列
每个 Java 对象关联一个 ObjectMonitor 结构,其中维护:
-
_EntryList(同步队列):存放因竞争锁失败而阻塞的线程(BLOCKED状态) -
_WaitSet(等待队列):存放已调用wait()并释放锁的线程(WAITING状态)
wait() 的底层动作是:先执行 monitorexit 释放锁,再将当前线程加入 _WaitSet;notify() 则是从 _WaitSet 中摘出一个线程,放入 _EntryList 或直接标记为可竞争状态。这两步操作都要求线程当前是 monitor 的 owner,否则无法安全修改队列结构。
不在 synchronized 中调用会导致“丢失唤醒”和虚假状态
没有同步块时,条件判断与 wait() 之间存在竞态窗口,典型错误链路如下:
AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。
- 消费者线程读取
queue.isEmpty() == true - CPU 切出,生产者线程获取锁、入队、调用
notify()(此时_WaitSet为空,信号被丢弃) - 消费者线程切回,执行
wait()并挂入_WaitSet,但再无唤醒机会
而 synchronized 块确保了「条件检查 + wait()」或「状态修改 + notify()」在 monitor 层面原子执行,避免信号发在无人监听时、也避免监听开始前信号已发出。
notify() 不是“发消息”,而是对 _WaitSet 的一次结构化操作
notify() 的实际行为取决于 ObjectMonitor 的 policy 配置(默认为 0),它决定从 _WaitSet 中选哪个线程移出——可能是 FIFO,也可能按优先级或 JVM 实现策略。关键点在于:
- 它只操作当前 monitor 的
_WaitSet,不能跨对象唤醒 - 它不改变任何共享变量,也不重试条件,只是把线程从等待态转为就绪态
- 被唤醒线程重新竞争锁成功后,仍需用
while循环再次校验条件,因为_WaitSet中的线程可能被虚假唤醒,或条件已被其他线程消费
真正容易被忽略的是:即使你写了 synchronized,如果锁对象和 wait()/notify() 调用的目标对象不一致(比如用 this 同步,却对 queue 调用 wait()),_WaitSet 和同步逻辑就完全错位了——这时不会报错,但永远等不到唤醒。










