wait方法需在synchronized块中调用以释放锁,必须用while循环检查条件防止虚假唤醒,notifyall比notify更适用于多生产者-消费者场景,且推荐使用带超时的wait避免永久阻塞。

Java 中 wait 方法本身不直接处理任务队列,而是配合 synchronized 和 notify/notifyAll 构建线程安全的阻塞式队列逻辑。它的核心作用是让线程在条件不满足时主动释放锁、进入等待状态,避免忙等或死锁。
wait 必须在 synchronized 块中调用
因为 wait 会释放当前持有的对象锁,所以调用前必须已获得该锁。否则抛出 IllegalMonitorStateException。例如向阻塞队列 put 元素时:
- 先用 synchronized(this) 获取队列对象锁
- 检查队列是否已满(
size == capacity) - 若满,调用
this.wait()—— 此刻锁被释放,线程挂起 - 其他线程 get 后腾出空间,再调用
this.notifyAll()唤醒等待者
必须用 while 而非 if 检查条件
唤醒不等于条件成立。多个线程可能同时被 notifyAll 唤醒,但只有第一个抢到锁的能真正执行;后续线程抢到锁后,队列可能又变满了(生产者场景)或又空了(消费者场景)。因此:
- 错误写法:
if (queue.isEmpty()) wait();→ 可能跳过二次检查,导致异常 - 正确写法:
while (queue.isEmpty()) wait();→ 每次醒来都重新验证条件
notifyAll 比 notify 更适合多线程队列
在生产者-消费者多对多场景下,notify 随机唤醒一个线程,存在“唤醒错人”的风险:
- 比如所有被唤醒的都是消费者,而生产者仍在 wait,队列持续为空 → 死锁倾向
- notifyAll 确保所有等待线程都有机会竞争锁和检查条件,更健壮
- 实际队列实现(如
ArrayBlockingQueue内部)也统一使用 notifyAll
wait 支持超时,防止永久阻塞
无参 wait() 是“死等”,一旦 notify 被遗漏或逻辑出错,线程将永远挂起。更稳妥的做法是使用带超时的版本:
-
wait(5000):最多等 5 秒,超时后自动恢复并再次检查条件 - 结合中断处理:
catch (InterruptedException e),响应外部取消信号 - 适用于对响应性有要求的队列,比如定时任务调度器中的待执行任务池
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











