Java中wait()为何必须同步调用?
在 Java 中,wait()
方法必须在 synchronized
方法或代码块中调用,主要原因如下:
1. 监视器锁(Monitor)机制
- 依赖对象锁:
wait()
方法需要操作对象的监视器锁(Monitor),调用前必须持有该对象的锁,否则会抛出IllegalMonitorStateException
。 - 释放锁:
wait()
会使当前线程释放锁并进入等待状态,只有持有锁的线程才能安全释放锁。
2. 避免竞态条件
- 原子性保障:
wait()
和notify()
的调用需要与共享变量的修改保持原子性。若不加锁,可能导致:- 竞态条件:线程 A 检查条件后、调用
wait()
前,线程 B 修改条件并调用notify()
,导致信号丢失(Missed Signal)。 - 虚假唤醒:线程可能因底层机制被意外唤醒,需通过循环检查条件避免。
- 竞态条件:线程 A 检查条件后、调用
3. 线程安全与可见性
- 同步保证可见性:
synchronized
确保线程对共享变量的修改对其他线程立即可见,避免因缓存不一致导致的条件判断错误。 - 防止并发冲突:若多个线程同时调用
wait()
或notify()
而无同步控制,会导致不可预测的行为。
4. 设计规范与异常处理
- JVM 强制约束:Java 规范明确要求
wait()
必须在同步上下文中调用,否则直接抛出异常。 - 锁管理一致性:
wait()
和notify()
的设计初衷是配合synchronized
实现线程协作,确保锁的获取与释放逻辑一致。
示例代码
synchronized (lock) {while (!condition) { // 循环检查条件,避免虚假唤醒lock.wait(); // 释放锁并等待}// 条件满足后执行操作
}
关键点:
- 调用
wait()
前必须通过synchronized
获取锁。 - 使用循环检查条件,而非
if
,确保唤醒后条件仍成立。
总结
原因 | 说明 |
---|---|
监视器锁依赖 | wait() 需持有锁才能操作对象监视器。 |
竞态条件避免 | 同步块保障条件检查与 wait() 调用的原子性。 |
线程安全与可见性 | synchronized 确保共享状态的一致性。 |
JVM 规范强制 | 非同步调用会抛出 IllegalMonitorStateException 。 |
通过 synchronized
的配合,wait()
能够安全实现线程间的协作与通信。