Java多线程通过wait/notify实现精确通信需满足:1.必须在synchronized块中调用;2.始终用while循环校验条件防虚假唤醒;3.根据场景选notify(单线程)或notifyAll(多线程更安全)。

Java 中多线程通过 wait 和 notify 实现精确通信,关键不在于“一次唤醒就万事大吉”,而在于构建**条件驱动、锁保护、循环校验**的协作闭环。它不是简单的“发信号→收信号”,而是围绕共享状态建立可信赖的等待-唤醒契约。
必须在 synchronized 块中调用
这是硬性前提,否则会抛 IllegalMonitorStateException。因为 wait 和 notify 操作的对象监视器(monitor)与锁强绑定:
-
wait()必须在已持有目标对象锁的前提下执行;调用后立即释放该锁,并将线程放入该对象的 WaitSet -
notify()同样需先获取锁;唤醒一个等待线程后,不会立刻释放锁,而是等当前同步块执行完才释放 - 两个线程必须操作同一个对象实例的锁,才能构成有效的通信通道
永远用 while 循环检查条件,不用 if
这是避免虚假唤醒(spurious wakeup)和竞态条件的核心实践。JVM 规范允许线程在未被显式通知时被唤醒,所以不能假设 wait 返回就意味着条件已满足:
- 错误写法:
if (count == 0) { lock.wait(); }—— 唤醒后直接消费,可能出错 - 正确写法:
while (count == 0) { lock.wait(); }—— 每次唤醒都重新验证条件 - 生产者同理:用
while (count == MAX)判断是否满仓,防止重复生产
notify 和 notifyAll 的选择要匹配业务语义
两者行为差异直接影响通信精度:
立即学习“Java免费学习笔记(深入)”;
-
notify()随机唤醒 WaitSet 中一个线程,适用于“单消费者/单生产者”且唤醒任意一个即可继续的场景 -
notifyAll()唤醒所有等待线程,由它们各自重新竞争锁并再次判断条件 —— 更安全,尤其适合多个同类线程共存(如多个消费者争抢任务)或条件复杂(如多个不同等待条件共用同一把锁)的情况 - 宁可多唤醒,不可漏唤醒。多数实际项目推荐优先使用
notifyAll,除非性能压测明确显示notify有显著优势且逻辑绝对可控
典型通信流程:以生产者-消费者为例
整个过程体现“状态变更 → 通知 → 竞争 → 校验 → 执行”的精确链条:
- 消费者进入
synchronized(lock),检查count == 0→ 成立 → 调用lock.wait()→ 释放锁,挂起 - 生产者获得锁,修改
count++,执行lock.notifyAll()→ WaitSet 中所有消费者被唤醒 - 被唤醒的消费者线程逐个尝试获取
lock;只有抢到锁的那个,才会再次执行while (count == 0)判断 - 若此时
count > 0,则消费并退出;若因其他线程抢先消费导致count又变回 0,则继续wait()


















