java中用condition实现多路线程精准唤醒,核心在于每个condition绑定独立reentrantlock,等待方用while+await检查状态,通知方按需调用signal()唤醒特定队列,避免虚假唤醒与竞争。

Java中用Condition实现多路线程精准唤醒,核心在于:每个Condition实例绑定到一个ReentrantLock上,不同业务线程等待各自专属的Condition,再由通知方选择性调用signal()或signalAll()——避免Object.wait()/notify()那种“全唤醒再竞争”的低效和不确定性。
一、先建好锁与多个Condition
一个ReentrantLock可关联多个Condition,每个代表一类等待场景。比如生产者/消费者模型中,可为“有空位”和“有数据”分别设条件:
- 用
lock.newCondition()创建独立Condition对象,命名要体现语义(如notFull、notEmpty) - 确保所有
await()和signal()操作都发生在lock.lock()和lock.unlock()之间 - 不要复用同一个
Condition来区分不同逻辑;否则无法做到“精准”唤醒
二、等待方必须用while+await配合状态检查
await()会释放锁并挂起线程,但被唤醒后不能直接执行后续逻辑——需重新获取锁,并再次验证前提条件是否真正满足(防止虚假唤醒或条件已变):
- 永远用
while (条件不满足) condition.await();,不用if - 例如消费者等待队列非空:
while (queue.isEmpty()) notEmpty.await(); - 唤醒后仍需在
lock保护下检查,确认数据确实可用再消费
三、通知方按需唤醒特定队列
只唤醒真正符合条件的线程组,是“精准”的关键:
- 新增元素后,调用
notFull.signal()唤醒等待入队的生产者 - 移除元素后,调用
notEmpty.signal()唤醒等待取数的消费者 - 若需唤醒所有等待该条件的线程(如中断全部等待者),用
signalAll() - 注意:
signal()不保证唤醒哪个线程,但保证只唤醒同Condition上的一个;这是可控的前提
四、避免常见陷阱
几个容易导致死锁或漏唤醒的细节:
- 不要在
finally块里调用signal()——可能唤醒尚未进入await()的线程,造成信号丢失 -
await()前未加锁,或signal()后未及时解锁,都会破坏同步契约 - 多个
Condition共用同一把锁时,signal()不会影响其他Condition上的等待线程,这点比notify()安全得多 - 调试时可用
getWaitQueueLength(condition)查看当前等待数量,辅助定位唤醒失效问题
写对锁、分清条件、守好循环检查、精准发信号——四步到位,就能让多路线程各等各的、各醒各的,不再抢着醒、醒了又等。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











