wait()必须在synchronized块中调用,因为其本质是“释放锁+进入等待队列”两个原子操作,需线程持有对象锁才能安全执行,否则抛illegalmonitorstateexception,并防止丢失唤醒。

因为 wait() 的本质是“释放锁 + 进入等待队列”两个原子动作,而 JVM 要求执行这个操作的线程必须是当前对象锁的持有者,否则无法安全释放、也无法正确挂入该对象的等待队列。
它依赖对象监视器(Monitor)的控制权
Java 中每个对象都关联一个监视器,只有获得该对象锁的线程,才有资格操作它的 wait set(等待队列)。synchronized 块正是获取这把锁的唯一合法方式。没拿锁就调用 wait(),JVM 根本不知道你要释放哪个锁、往哪个队列里挂,直接抛 IllegalMonitorStateException。
防止“丢失唤醒”(Lost Wakeup)
这是最关键的业务逻辑原因:如果条件检查和 wait() 不在同一个同步块里,就会出现竞态。
- 线程 A 判断条件不满足,正准备调用 wait()
同步块把“检查条件 → 决定等待”打包成原子操作,确保 notify() 不会落在 wait() 之前。
保证共享状态的可见性与一致性
在 synchronized 块中修改的变量,对其他线程是立即可见的;而 wait()/notify() 往往配合某个布尔条件(比如 isReady == false)使用。只有在锁保护下读写这个条件,才能避免一个线程看到过期值,另一个线程已更新却未被感知。
不是语法限制,而是协作机制的设计契约
wait 和 notify 不是普通方法,它们是线程间通信的原语,天然绑定在监视器模型上。就像你不能在没开门的情况下要求把东西放进保险箱——synchronized 是开门动作,wait 是放东西进等待区的动作,两者缺一不可。











