notify并非随机唤醒,而是由JVM实现决定唤醒任意一个等待线程,不保证公平性、随机性或可预测性;需自行管理线程集合实现真随机唤醒。

Object.notify 从不随机唤醒线程——它唤醒的是 JVM 自己选的一个,但这个选择既不保证公平性,也不保证随机性,更不暴露给开发者控制。
为什么 notify 不是“随机唤醒”
Java 规范只规定:notify 唤醒“某个正在此对象监视器上等待的单个线程”,至于唤醒谁,由 JVM 实现决定(HotSpot 通常按入队顺序选,但不承诺 FIFO;ZGC 或某些低延迟 GC 下可能更不可预测)。它不是基于随机数生成器挑的,也不是轮询式选的,更不是你调用一次就“掷骰子”一次。
- 没有
Random参与,也没有可配置的调度策略 - 无法通过参数、系统属性或反射影响其选择逻辑
- 多线程竞争下看似“随机”,实则是调度不确定性 + JVM 内部队列行为叠加的结果
想实现真正随机唤醒,得自己管理等待线程
如果你的业务逻辑确实需要“从 N 个等待者中随机挑一个唤醒”,必须绕过内置监视器机制,改用显式数据结构维护等待线程引用。
- 用
ConcurrentLinkedQueue或CopyOnWriteArrayList存储Thread对象(注意:存储前需确保线程处于WAITING状态) - 唤醒时用
Thread.interrupt()或自定义标志位 +notify()配合条件检查 - 典型模式:每个等待线程在循环中检查共享 flag,再调用
wait();唤醒方设 flag 后调用notifyAll(),但只让一个线程真正退出循环
示例关键片段:
private final List<Thread> waiters = new CopyOnWriteArrayList<>();
// 等待方
synchronized (lock) {
waiters.add(Thread.currentThread());
while (!ready) lock.wait();
waiters.remove(Thread.currentThread());
}
// 唤醒方(真随机)
if (!waiters.isEmpty()) {
int idx = ThreadLocalRandom.current().nextInt(waiters.size());
waiters.get(idx).interrupt(); // 或设 volatile flag 并 notifyAll()
}
误用 notify 的典型后果
把 notify 当作“随机唤醒工具”用,往往掩盖了并发设计缺陷:
- 用
notify替代notifyAll()却没确保所有等待线程的条件互斥 → 某些线程永久沉睡(lost wakeup) - 依赖“唤醒顺序”做业务逻辑(比如轮询任务分发)→ 在不同 JDK 版本或 GC 模式下行为漂移
- 测试时看似正常(小压力下碰巧唤醒了想要的线程),上线后因线程调度变化导致偶发故障
真正需要可控唤醒时,别依赖 JVM 黑盒行为;notify 的语义就是“唤醒任意一个”,不是“随机唤醒”,也不是“唤醒下一个”。它的不可控性恰恰是设计使然——留给上层用更清晰的并发原语(如 Phaser、BlockingQueue、StampedLock)去表达意图。

















