notify()无法直接避免线程饥饿,需配合notifyall、while循环条件检查及公平调度机制;notify仅随机唤醒单个线程,易导致部分线程长期挂起,而notifyall唤醒所有线程并重检条件,显著降低饥饿概率。

Java 中 notify() 本身无法直接避免线程饥饿,它只是随机唤醒一个等待线程,若使用不当(如未配合公平条件、未重入检查或未循环等待),就容易导致某些线程长期得不到唤醒。真正缓解饥饿需靠设计层面的协同机制。
用 notifyAll 替代 notify(多数场景更稳妥)
notify() 只唤醒单个任意等待线程,JVM 不保证公平性,可能反复唤醒同一活跃线程,而其他线程持续挂起。相比之下,notifyAll() 唤醒所有等待线程,让它们重新竞争锁并按需判断条件是否满足,显著降低饥饿概率。
- 尤其适用于多个线程等待不同条件(如生产者/消费者中多个消费者等待不同数据)
- 虽有性能开销,但在多数业务场景下可接受;现代 JVM 对大量线程唤醒做了优化
- 必须配合 while 循环检查条件,防止虚假唤醒和条件不满足时继续执行
确保 wait 前条件已校验,且 wait 后仍用 while 重检
饥饿常源于线程在条件不成立时被唤醒却直接执行——这往往因用了 if 而非 while 判断条件。
- 错误写法:
if (!ready) wait();→ 唤醒后跳过条件检查,可能出错 - 正确写法:
while (!ready) wait();→ 即使被唤醒也重新判断,不满足则继续等待 - 该模式是 Java 等待-通知机制的强制约定,不是可选项
引入显式队列或轮询机制实现调度公平性
当业务逻辑要求严格顺序(如 FIFO)时,仅靠内置 monitor 无法保证。可借助外部结构增强公平性:
- 用
LinkedBlockingQueue或ArrayBlockingQueue(构造时启用fair = true)替代手动 wait/notify - 自定义等待队列:将等待线程封装为节点入队,唤醒时按队首出队,再调用其
notify() - 避免在 synchronized 块内做复杂逻辑,缩短临界区,减少“唤醒后抢不到锁”的间接饥饿
慎用 notify,明确适用边界
notify() 并非“错误”,而是在特定前提下才安全:
- 仅有一个线程在等待,或多个线程等待的是完全相同且互斥的条件
- 唤醒后,被唤醒线程一定能推进状态(例如:唯一消费者等待缓冲区非空)
- 已通过其他手段确保不会出现“唤醒无效线程”(如用标志位区分等待类型)
- 否则,默认优先选
notifyAll()+while条件检查,更鲁棒
不复杂但容易忽略:线程饥饿不是 notify 的 bug,而是同步逻辑与条件判断松耦合的结果。把条件检查、唤醒策略、锁粒度三者对齐,比纠结用 notify 还是 notifyAll 更关键。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











