Java中的条件变量(Condition)详解
字数 1099 2025-11-13 07:49:28
Java中的条件变量(Condition)详解
一、条件变量的基本概念
条件变量(Condition)是Java并发包中用于实现线程间协调的重要工具,它与锁(Lock)结合使用,可以替代传统的Object.wait()/notify()机制,提供更灵活的线程等待/通知功能。
二、为什么需要条件变量
- synchronized的局限性:使用synchronized时,所有等待线程都在同一个等待队列中,notify()会随机唤醒一个线程,notifyAll()会唤醒所有线程,无法精确控制唤醒特定类型的线程。
- 条件谓词:在多线程编程中,经常需要线程在某个"条件"满足时才执行(如队列非空才能取数据),条件变量允许为不同的条件创建不同的等待队列。
三、条件变量的核心方法
public interface Condition {
void await() throws InterruptedException; // 等待,响应中断
void awaitUninterruptibly(); // 不响应中断的等待
long awaitNanos(long nanosTimeout) throws InterruptedException; // 限时等待
boolean await(long time, TimeUnit unit) throws InterruptedException;
void signal(); // 唤醒一个等待线程
void signalAll(); // 唤醒所有等待线程
}
四、条件变量的使用步骤
- 创建条件变量:通过Lock.newCondition()方法创建
- 获取锁:在调用await/signal前必须先获取锁
- 检查条件:在await前必须检查条件(通常用while循环)
- 等待条件:条件不满足时调用await()
- 通知等待线程:条件满足时调用signal()/signalAll()
五、经典示例:有界队列实现
public class BoundedQueue<T> {
private final Object[] items;
private int count = 0;
private int putIndex = 0;
private int takeIndex = 0;
private final Lock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition(); // 队列非空条件
private final Condition notFull = lock.newCondition(); // 队列未满条件
public BoundedQueue(int capacity) {
this.items = new Object[capacity];
}
// 入队操作
public void put(T item) throws InterruptedException {
lock.lock();
try {
// 使用while而不是if,防止虚假唤醒
while (count == items.length) {
notFull.await(); // 队列已满,等待"未满"条件
}
items[putIndex] = item;
if (++putIndex == items.length) putIndex = 0;
count++;
notEmpty.signal(); // 通知等待"非空"条件的线程
} finally {
lock.unlock();
}
}
// 出队操作
public T take() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
notEmpty.await(); // 队列为空,等待"非空"条件
}
@SuppressWarnings("unchecked")
T item = (T) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length) takeIndex = 0;
count--;
notFull.signal(); // 通知等待"未满"条件的线程
return item;
} finally {
lock.unlock();
}
}
}
六、关键要点解析
-
为什么要用while循环检查条件:
- 防止虚假唤醒:某些实现可能在没有调用signal的情况下唤醒线程
- 条件可能被其他线程改变:被唤醒后需要重新检查条件是否真正满足
-
signal()与signalAll()的选择:
- signal():效率更高,但只唤醒一个线程
- signalAll():更安全,确保所有等待的线程都有机会检查条件
- 通常建议使用signalAll(),除非性能要求严格
-
与Object监视器方法的对比:
- Condition.await() ≈ Object.wait()
- Condition.signal() ≈ Object.notify()
- Condition.signalAll() ≈ Object.notifyAll()
- 但Condition支持多个等待队列,更灵活
七、高级特性
-
限时等待:避免永久阻塞
if (condition.await(1, TimeUnit.SECONDS)) { // 在指定时间内被唤醒 } else { // 超时处理 } -
不可中断等待:awaitUninterruptibly()用于不响应中断的场景
-
公平性与非公平性:Condition的公平性取决于创建它的Lock对象
八、最佳实践
- 总是在finally块中释放锁
- 使用while循环而不是if检查条件
- 优先考虑使用signalAll()确保正确性
- 考虑使用限时等待避免死锁
通过条件变量,我们可以实现更精细的线程间协调,解决复杂的同步问题,这是构建高性能并发组件的重要基础。