java线程通信
一、wait /notify/notifyAll 底层原理
1. 核心前提
- 必须在synchronized 锁内部使用,否则直接抛
IllegalMonitorStateException - 调用
wait()会自动释放当前锁,进入对象的 WaitSet 等待池 - 被唤醒后不会立刻执行,需要重新竞争锁,抢到锁才会继续往下走
2. 方法作用
obj.wait()
当前线程进入无限等待,释放锁,等待被唤醒;
支持重载:带超时时间的限时等待。
obj.notify()
随机唤醒 WaitSet 中一个等待线程。
obj.notifyAll()
唤醒 WaitSet 全部等待线程,全员去竞争锁。
3. 致命缺点
- 唤醒不精准:只能随机 / 全部唤醒,无法指定唤醒某个线程
- 多生产多消费场景:容易虚假唤醒、唤醒不该唤醒的线程,造成低效 / 死锁
- 只能依附
synchronized原生锁,功能单一,不支持超时、不支持多条件队列
4. 虚假唤醒
线程没有被 notify / 中断 / 超时,无缘无故从 wait 醒来;
开发规范:wait 必须写在 while 循环里做条件判断,醒来二次校验条件。
二、Condition 精准唤醒(JUC 升级版)
1. 定位
Condition 是 Lock(ReentrantLock) 配套的条件等待工具,
用来彻底解决 wait/notify 唤醒模糊的问题。
2. 核心优势
- 精准唤醒:多个 Condition 隔离不同等待队列,唤醒指定业务线程
- 多条件队列:一把锁可以创建多个 Condition,各司其职
- 功能更强:支持可中断等待、超时等待、不响应中断等待
- 搭配 Lock 锁,灵活度远高于 synchronized + wait
3. 核心方法
condition.await():等价 wait,释放锁、进入等待队列condition.signal():等价 notify,精准唤醒当前队列一个线程condition.signalAll():等价 notifyAll,唤醒当前队列全部线程
4. 底层关系
AQS 内部维护:
同步队列:抢锁失败排队
条件队列:Condition.await 等待的队列
等待时:同步队列 → 条件队列;
唤醒时:条件队列 → 同步队列 → 重新抢锁。
三、生产者消费者模型(两种实现)
方式 1:synchronized + wait/notifyAll 实现
1 | |
缺点:
生产满了、消费空了,全都挤在同一个 WaitSet;
notifyAll 一窝蜂唤醒,大量无效竞争,性能差。
方式 2:ReentrantLock + Condition 精准唤醒(推荐)
两个条件队列:
notFull:缓冲区未满 → 生产者等待 / 唤醒notEmpty:缓冲区不为空 → 消费者等待 / 唤醒
1 | |
核心优势
- 队列满了,只阻塞生产者;只唤醒消费者
- 队列空了,只阻塞消费者;只唤醒生产者
- 无无效唤醒、无无效锁竞争,并发性能更高
四、核心对比 & 原理总结
wait/notify
绑定对象 Monitor 的 WaitSet,唤醒随机 / 全体,粒度粗、低效,依赖 synchronized。
Condition/await/signal
绑定 AQS 条件队列,多队列隔离、精准唤醒,搭配 Lock,功能更强、工业级使用。
生产者消费者核心规范
- 条件等待必须用
while循环,防御虚假唤醒 - 等待前:判断阻塞条件
- 唤醒后:二次校验条件,避免并发错乱
- 条件等待必须用
本质通信逻辑
多线程争抢同一共享资源 → 不满足条件就释放锁并等待 → 条件满足被唤醒 → 重新抢锁执行业务。
java线程通信
http://hanqichuan.com/2026/04/16/java并发/java线程通信/