Java中wait()的正确唤醒机制是“条件驱动+循环检查+同步保护”,必须用while循环而非if检查条件,因存在虚假唤醒和竞争导致条件失效;notify/notifyAll须在synchronized块内调用,唤醒后线程需重新竞争锁才能继续执行。

Java 中 wait() 的正确唤醒机制,核心在于“条件驱动 + 循环检查 + 同步保护”。它不是简单地等一个 notify() 就往下走,而是要确保线程只在真正满足业务条件时才继续执行。
必须用 while 循环检查条件
不能用 if,这是最关键的实践。因为:
- 存在虚假唤醒(spurious wakeup):JVM 或操作系统可能无理由唤醒等待线程,此时条件很可能仍未满足
- 多个线程竞争同一资源时,某个线程被唤醒后,条件可能已被其他线程抢先改变(比如缓冲区又被消费空了)
- 使用
while可保证每次从wait()返回后,都重新验证逻辑条件是否成立
正确写法示例:
synchronized (lock) {
while (!conditionMet()) {
lock.wait();
}
// 此处 conditionMet() 为 true,安全执行后续操作
}
唤醒操作必须由持有锁的线程发起
notify() 和 notifyAll() 不是“发个信号就完事”,它们有严格前提:
立即学习“Java免费学习笔记(深入)”;
- 调用者必须已获得该对象的监视器锁(即在
synchronized(lock)块内) - 否则抛出
IllegalMonitorStateException - 唤醒动作本身不释放锁;当前线程会继续执行完同步块,之后锁才被释放,被唤醒线程才能参与锁竞争
典型配合模式:
// 生产者线程
synchronized (lock) {
produceItem();
lock.notifyAll(); // 或 notify()
}
// 消费者线程
synchronized (lock) {
while (isEmpty()) {
lock.wait();
}
consumeItem();
}
wait 后线程状态与锁竞争流程
一次完整的 wait() 到恢复执行,涉及三阶段状态转换:
-
挂起并释放锁:调用
wait()后,线程立即释放锁,状态变为WAITING(或TIMED_WAITING),进入对象的等待队列(Wait Set) -
被唤醒但不立即执行:
notify()把线程移出 Wait Set,放入锁竞争队列(Lock Queue);此时线程状态为BLOCKED -
重新获取锁后继续:只有成功抢到锁,线程才回到
RUNNABLE,从wait()调用处继续向下执行
推荐优先使用 notifyAll()
除非明确知道只有一个线程在等待,否则应倾向用 notifyAll():
-
notify()随机唤醒一个,若唤醒的是不关心该条件的线程,可能导致其余等待线程永久阻塞 - 在复杂条件(如多个不同等待条件共用同一锁)下,
notifyAll()更安全,让所有等待者自行判断是否符合条件 - 性能差异在绝大多数场景中可忽略;死锁风险远比轻微开销更值得规避


















