必须用 while 循环包裹 wait(),因为线程唤醒后不能假设条件已满足,需防范虚假唤醒、竞争覆盖和 notify 提前丢失;标准写法是在循环中先判断条件再 wait,唤醒后重新检查。

Java 中 wait() 方法本身不实现状态检测,它只是让线程释放锁并进入等待状态;真正负责循环判断状态的是调用 wait() 的外层逻辑——必须用 while 循环而非 if,才能安全应对虚假唤醒和条件变化。
为什么必须用 while 循环包裹 wait()
线程被唤醒后,不能假设“唤醒即条件满足”。可能因以下原因导致条件仍未成立:
- 虚假唤醒(spurious wakeup):JVM 或操作系统可能在没有 notify/notifyAll 的情况下唤醒线程;
- 竞争导致条件被覆盖:多个等待线程被同时唤醒,但只有第一个能成功修改共享状态,其余线程需重新检查;
- notify 被提前调用:生产者在消费者尚未 wait 前就 notify,该次通知丢失,后续 wait 就得靠循环重检。
标准写法:wait 必须放在 while 循环中
典型模式如下(以阻塞队列的 take() 为例):
synchronized (lock) {
while (queue.isEmpty()) {
try {
lock.wait(); // 释放锁,等待
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
return queue.poll();
}
关键点:
立即学习“Java免费学习笔记(深入)”;
- 检查条件(
queue.isEmpty())在wait()前后都发生; - 每次唤醒后重新判断,不跳过验证;
- 确保只有条件真正满足时才继续执行业务逻辑。
notify 和 notifyAll 的选择影响循环行为
使用 notify() 只唤醒一个等待线程,但无法保证它对应的状态已就绪;notifyAll() 唤醒所有线程,靠各自的 while 循环各自重检条件。实践中:
- 若等待条件互斥(如“队列非空”只有一个含义),
notifyAll()更安全; - 若能精确匹配等待者与通知场景(极少见),可谨慎用
notify(); - 误用
notify()加if判断极易导致死锁或永久等待。
和 Condition.await() 的对比
Condition 是对 wait/notify 的封装,语义更清晰,但同样要求 while 循环:
lock.lock();
try {
while (!conditionMet) {
condition.await();
}
// 执行操作
} finally {
lock.unlock();
}
底层仍是 Object 的 wait(),所以循环判断逻辑不可省略——这是并发编程的基本契约,不是语法限制,而是正确性的必需。


















