详解JUC包中的Condition接口
Java的Condition接口提供了比Object.wait()/notify()更精细的线程协调机制,主要特点包括:1)一个Lock可创建多个Condition,实现多条件精准唤醒;2)支持await()、signal()等方法,可响应中断;3)基于AQS实现,通过独立等待队列管理线程。典型应用如生产者-消费者模型,使用时需注意循环检查条件、避免死锁等最佳实践。Condition通过多条件队列
·
Java中的Condition接口是java.util.concurrent.locks包下的核心组件,与Lock配合使用,提供比传统Object.wait()/notify()更灵活、精细的线程协调机制。
以下从多维度详解其原理、用法及最佳实践:
1. 核心概念与优势
- 多条件队列:一个
Lock可创建多个Condition对象(如notEmpty、notFull),每个对应独立等待队列,实现多线程按条件精准唤醒,避免“惊群效应”(如生产者-消费者模型中,缓冲区满时生产者等待,空时消费者等待)。 - 灵活控制:支持
await()(无限等待)、await(long time, TimeUnit unit)(超时等待)、signal()(唤醒单个线程)、signalAll()(唤醒全部线程)等方法,且await()可响应中断。 - 与AQS深度集成:基于
AbstractQueuedSynchronizer(AQS)实现,等待线程释放锁后加入Condition队列,被唤醒后重新竞争锁。
2. 核心方法与行为
await():- 线程必须持有锁,否则抛出
IllegalMonitorStateException。 - 释放锁并加入
Condition等待队列,进入WAITING状态,直到被signal()或中断唤醒。 - 唤醒后需重新获取锁,从
await()返回时保证持有锁。
- 线程必须持有锁,否则抛出
signal()/signalAll():- 唤醒
Condition队列头部线程(或全部线程),将其移至AQS同步队列参与锁竞争。 - 需在持有锁时调用,否则抛出异常。
- 唤醒
- 其他方法:
awaitUninterruptibly():不可中断等待。awaitNanos()/awaitUntil():指定纳秒级超时或截止时间。
3. 内部实现原理
- 等待队列结构:
Condition内部维护单向链表队列(firstWaiter/lastWaiter),存储调用await()的线程节点(Node)。 await()流程:- 构造
Node加入Condition队列尾部。 - 释放锁(通过
fullyRelease方法),唤醒AQS队列中后续线程。 - 线程阻塞(
LockSupport.park),直到被signal()移至AQS队列。 - 唤醒后重新竞争锁,成功则从
await()返回。
- 构造
signal()流程:- 获取
Condition队列头节点,移除并链接至AQS队列尾部。 - 唤醒线程参与锁竞争,若竞争成功则继续执行。
- 获取
4. 典型应用场景
- 生产者-消费者模型:
class BoundedBuffer<T> { private Queue<T> buffer = new LinkedList<>(); private int capacity; private Lock lock = new ReentrantLock(); private Condition notFull = lock.newCondition(); private Condition notEmpty = lock.newCondition(); public void put(T item) throws InterruptedException { lock.lock(); try { while (buffer.size() == capacity) notFull.await(); // 缓冲区满,生产者等待 buffer.offer(item); notEmpty.signalAll(); // 唤醒消费者 } finally { lock.unlock(); } } public T take() throws InterruptedException { lock.lock(); try { while (buffer.isEmpty()) notEmpty.await(); // 缓冲区空,消费者等待 T item = buffer.poll(); notFull.signalAll(); // 唤醒生产者 return item; } finally { lock.unlock(); } } } - 多条件协调:如餐厅订单系统,厨师、服务员、顾客分别等待“食材就绪”“菜品就绪”“订单完成”等不同条件。
5. 最佳实践与注意事项
- 锁与条件绑定:通过
Lock.newCondition()创建Condition,确保操作前已获取锁。 - 循环检查条件:用
while而非if检查条件(如while (!conditionMet) await()),防止虚假唤醒。 - 避免死锁:确保
await()和signal()调用顺序合理,避免线程相互等待。 - 中断处理:
await()可响应中断,需捕获InterruptedException并恢复中断状态。 - 性能优化:
signalAll()在需要唤醒所有线程时使用,否则优先signal()减少竞争。
6. 与Object.wait()/notify()对比
| 特性 | Condition | Object.wait()/notify() |
|---|---|---|
| 锁关联 | 需显式与Lock绑定 |
隐式与对象监视器绑定 |
| 条件队列数量 | 支持多个独立队列 | 仅一个全局队列 |
| 唤醒精度 | 可指定唤醒单个或全部线程 | notify()随机唤醒,notifyAll()唤醒全部 |
| 中断响应 | await()可中断 |
wait()可中断 |
| 虚假唤醒处理 | 需循环检查条件 | 需循环检查条件 |
总结:Condition通过多条件队列、灵活唤醒策略及与AQS的深度集成,为复杂线程协作场景提供高效解决方案。掌握其用法需理解锁机制、条件检查循环及内部队列管理,结合生产者-消费者等经典模式实践,可显著提升并发程序的可控性与性能。
更多推荐



所有评论(0)