核心问题是多线程对共享状态的非原子性操作引发竞态,需用while循环校验条件、synchronized保证wait/notify原子性、volatile或原子类保障变量安全、状态机替代全局flag。

核心问题不是“next唤醒变量”本身,而是循环中多个线程同时读取、判断、修改同一共享状态(比如 flag、counter 或锁标识),且未用同步机制保护整个检查-等待-执行流程。崩溃和污染的根源在于条件判断与后续操作之间存在竞态窗口。
用 while 替代 if 进行条件等待
Java 中 wait() 必须配合 while 循环使用,不能只用 if ——这是防止虚假唤醒和并发写污染的第一道防线。
- if 判断只执行一次:线程被唤醒后直接往下走,不管条件是否真满足,可能操作已失效或被覆盖的状态
- while 持续校验:每次从 wait() 返回都重新检查条件,确保业务逻辑只在真实就绪时执行
- 示例修正:
while (!flag) { lock.wait(); },而非if (!flag) { lock.wait(); }
确保 wait/notify 在同一把锁下原子执行
wait() 和 notify() 必须在 synchronized 块内调用,且锁对象必须完全一致;否则会出现 IllegalMonitorStateException 或唤醒失效。
- 所有对共享变量 flag 的读写、所有 wait() 和 notify() 调用,都必须包裹在同一个 lock 对象的 synchronized 块中
- 避免混用不同锁对象(如用 lock1 等待,却用 lock2 通知)
- 不要在锁外修改 flag 后再进锁 notify —— 修改和通知必须原子化
避免共享变量被多线程非原子更新
如果 “next 唤醒变量” 是一个计数器、索引或状态标记(如 nextId、readyIndex),它本身也需线程安全访问。
- 普通 int/boolean 不具备原子性:++、赋值+判断等操作在多线程下会丢失更新
- 简单场景用 volatile + 原子类:如
AtomicInteger nextIndex = new AtomicInteger(0),用nextIndex.incrementAndGet() - 复杂状态变更(如多字段联动)应封装进 synchronized 方法或使用 ReentrantLock
用显式状态机替代隐式 flag 轮询
靠单个布尔 flag 控制唤醒容易退化为“忙等”或漏通知;更健壮的方式是将状态变迁建模为有限状态机,并绑定到具体任务实例。
- 为每个任务分配唯一 ID,状态存于 ConcurrentHashMap<String, TaskState>,避免全局 flag 冲突
- 唤醒时指定目标 taskKey:
stateMap.put(taskKey, READY); notifyTask(taskKey); - 等待线程只监听自己 key 的状态变化,不依赖全局变量,彻底隔离污染面

















