Java中的条件变量(Condition)详解
字数 1099 2025-11-13 07:49:28

Java中的条件变量(Condition)详解

一、条件变量的基本概念
条件变量(Condition)是Java并发包中用于实现线程间协调的重要工具,它与锁(Lock)结合使用,可以替代传统的Object.wait()/notify()机制,提供更灵活的线程等待/通知功能。

二、为什么需要条件变量

  1. synchronized的局限性:使用synchronized时,所有等待线程都在同一个等待队列中,notify()会随机唤醒一个线程,notifyAll()会唤醒所有线程,无法精确控制唤醒特定类型的线程。
  2. 条件谓词:在多线程编程中,经常需要线程在某个"条件"满足时才执行(如队列非空才能取数据),条件变量允许为不同的条件创建不同的等待队列。

三、条件变量的核心方法

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();                          // 唤醒所有等待线程
}

四、条件变量的使用步骤

  1. 创建条件变量:通过Lock.newCondition()方法创建
  2. 获取锁:在调用await/signal前必须先获取锁
  3. 检查条件:在await前必须检查条件(通常用while循环)
  4. 等待条件:条件不满足时调用await()
  5. 通知等待线程:条件满足时调用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();
        }
    }
}

六、关键要点解析

  1. 为什么要用while循环检查条件

    • 防止虚假唤醒:某些实现可能在没有调用signal的情况下唤醒线程
    • 条件可能被其他线程改变:被唤醒后需要重新检查条件是否真正满足
  2. signal()与signalAll()的选择

    • signal():效率更高,但只唤醒一个线程
    • signalAll():更安全,确保所有等待的线程都有机会检查条件
    • 通常建议使用signalAll(),除非性能要求严格
  3. 与Object监视器方法的对比

    • Condition.await() ≈ Object.wait()
    • Condition.signal() ≈ Object.notify()
    • Condition.signalAll() ≈ Object.notifyAll()
    • 但Condition支持多个等待队列,更灵活

七、高级特性

  1. 限时等待:避免永久阻塞

    if (condition.await(1, TimeUnit.SECONDS)) {
        // 在指定时间内被唤醒
    } else {
        // 超时处理
    }
    
  2. 不可中断等待:awaitUninterruptibly()用于不响应中断的场景

  3. 公平性与非公平性:Condition的公平性取决于创建它的Lock对象

八、最佳实践

  1. 总是在finally块中释放锁
  2. 使用while循环而不是if检查条件
  3. 优先考虑使用signalAll()确保正确性
  4. 考虑使用限时等待避免死锁

通过条件变量,我们可以实现更精细的线程间协调,解决复杂的同步问题,这是构建高性能并发组件的重要基础。

Java中的条件变量(Condition)详解 一、条件变量的基本概念 条件变量(Condition)是Java并发包中用于实现线程间协调的重要工具,它与锁(Lock)结合使用,可以替代传统的Object.wait()/notify()机制,提供更灵活的线程等待/通知功能。 二、为什么需要条件变量 synchronized的局限性 :使用synchronized时,所有等待线程都在同一个等待队列中,notify()会随机唤醒一个线程,notifyAll()会唤醒所有线程,无法精确控制唤醒特定类型的线程。 条件谓词 :在多线程编程中,经常需要线程在某个"条件"满足时才执行(如队列非空才能取数据),条件变量允许为不同的条件创建不同的等待队列。 三、条件变量的核心方法 四、条件变量的使用步骤 创建条件变量 :通过Lock.newCondition()方法创建 获取锁 :在调用await/signal前必须先获取锁 检查条件 :在await前必须检查条件(通常用while循环) 等待条件 :条件不满足时调用await() 通知等待线程 :条件满足时调用signal()/signalAll() 五、经典示例:有界队列实现 六、关键要点解析 为什么要用while循环检查条件 : 防止虚假唤醒:某些实现可能在没有调用signal的情况下唤醒线程 条件可能被其他线程改变:被唤醒后需要重新检查条件是否真正满足 signal()与signalAll()的选择 : signal():效率更高,但只唤醒一个线程 signalAll():更安全,确保所有等待的线程都有机会检查条件 通常建议使用signalAll(),除非性能要求严格 与Object监视器方法的对比 : Condition.await() ≈ Object.wait() Condition.signal() ≈ Object.notify() Condition.signalAll() ≈ Object.notifyAll() 但Condition支持多个等待队列,更灵活 七、高级特性 限时等待 :避免永久阻塞 不可中断等待 :awaitUninterruptibly()用于不响应中断的场景 公平性与非公平性 :Condition的公平性取决于创建它的Lock对象 八、最佳实践 总是在finally块中释放锁 使用while循环而不是if检查条件 优先考虑使用signalAll()确保正确性 考虑使用限时等待避免死锁 通过条件变量,我们可以实现更精细的线程间协调,解决复杂的同步问题,这是构建高性能并发组件的重要基础。