wait()和notify()必须在synchronized块中调用,因为它们操作对象监视器锁,要求线程必须是锁持有者才能释放锁、进入等待队列、安全唤醒及防止竞态与虚假唤醒。

因为它们操作的是对象的监视器锁(monitor),而 JVM 强制要求调用线程必须是该锁的持有者,否则直接抛 IllegalMonitorStateException。这不是语法限制,而是机制设计的必然要求。
确保锁能被合法释放和重入
wait() 的语义包含三个原子动作:释放锁 → 进入等待队列 → 挂起线程。释放锁的前提是“已经持有锁”。如果不在 synchronized 块中调用,线程根本没拿到锁,就谈不上释放;同样,被唤醒后也需重新竞争并获取同一把锁才能继续执行——这个过程依赖 JVM 对监视器的精确追踪,只有在同步上下文中才能保证。
防止条件检查与等待之间的竞态
实际使用中,wait() 几乎总是在某个条件不满足时才调用,比如:
- 缓冲区为空时消费者调用 wait()
- 缓冲区已满时生产者调用 wait()
若检查条件和调用 wait() 不在同一个同步块里,中间可能被其他线程修改状态,导致“条件刚满足就被跳过”或“条件已变却还在等”。同步块让判断和等待成为原子操作,避免状态不一致。
立即学习“Java免费学习笔记(深入)”;
保证 notify/notifyAll 能正确唤醒目标线程
notify() 并不是“发个信号就完事”,它会把等待队列里的线程移到入口队列,让它们去竞争锁。这个转移动作本身需要持有对象锁,否则无法安全操作等待队列。更重要的是,只有在同一个对象、同一个同步上下文中调用 wait 和 notify,JVM 才能确保唤醒的是真正等待该条件的线程。
规避虚假唤醒与死锁风险
即使被唤醒,线程也要重新检查条件(所以要用 while 而非 if)。这个重检必须在同步块内完成,否则可能读到过期值。另外,脱离同步调用 notify 可能导致唤醒无效——比如通知发出去了,但接收方还没进入 wait,或者根本不在该对象的等待队列中,最终造成线程永久等待或资源僵死。


















