Java多线程:为什么wait()必须用循环而非if?
在Java多线程编程中,调用wait()
方法时应使用**循环结构(while循环)**而非if块,这是由线程同步的特性和潜在风险决定的。以下是具体原因和实现规范:
一、正确调用方式
synchronized (lockObject) {while (!condition) { // 必须使用循环检查条件try {lockObject.wait();} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 恢复中断状态}}// 条件满足后执行逻辑
}
二、必须使用循环的核心原因
-
防止虚假唤醒(Spurious Wakeup)
- 线程可能在没有收到
notify()
/notifyAll()
的情况下被操作系统或JVM意外唤醒。 - 循环会重复检查条件,确保唤醒后条件真正满足。
- 线程可能在没有收到
-
应对条件竞争
- 多线程环境下,即使线程被合法唤醒,共享资源可能已被其他线程修改(如多个消费者竞争同一资源)。
- if块仅检查一次条件,可能导致线程在条件不满足时继续执行。
-
规范要求
- Java官方文档明确建议
wait()
应在循环中调用,这是线程同步的最佳实践。
- Java官方文档明确建议
三、关键注意事项
-
同步块约束
wait()
必须在synchronized
块内调用,否则会抛出IllegalMonitorStateException
。- 调用
wait()
会释放当前持有的锁,唤醒后需重新获取锁。
-
中断处理
- 需捕获
InterruptedException
并妥善处理(如恢复中断状态)。
- 需捕获
-
与
notify()
配合- 通常优先使用
notifyAll()
而非notify()
,避免线程饥饿。
- 通常优先使用
四、错误示例对比
// 错误:if块无法应对虚假唤醒和条件竞争
synchronized (lock) {if (!condition) {lock.wait(); // 唤醒后直接执行后续代码,可能条件仍未满足}
}
总结:循环调用
wait()
是线程安全的必要条件,能有效解决虚假唤醒、条件竞争等问题,确保程序逻辑的可靠性。