Skip to content

Condition 接口

比 wait/notify 更灵活的条件等待。

为什么需要 Condition?

Object.wait()/notify() 只有一个条件队列,如果需要多个等待条件怎么办?

java
// wait/notify 的问题:只有一个条件队列
synchronized (lock) {
    // 队列满时等
    while (queue.isFull()) {
        lock.wait();
    }
    // 但唤醒后,可能队列还是满的(多个生产者都被唤醒了)
}

Condition 可以创建多个条件队列

java
Lock lock = new ReentrantLock();
Condition notFull = lock.newCondition();   // 队列不满
Condition notEmpty = lock.newCondition();   // 队列不空
Condition done = lock.newCondition();      // 完成信号

Condition vs Object wait/notify

对比Object wait/notifyCondition
条件队列每个对象一个每个锁可创建多个
公平性不可控可选公平/非公平
超时等待wait(timeout)await(timeout, TimeUnit)
中断支持支持支持,且有 awaitUninterruptibly()

基本用法

java
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

lock.lock();
try {
    while (!conditionMet) {
        condition.await();  // 等
    }
    // 执行任务
    condition.signal();  // 通知
} finally {
    lock.unlock();
}

Condition 方法一览

方法作用
await()等待,可被中断
awaitUninterruptibly()等待,不响应中断
awaitNanos(long nanos)等待指定纳秒数
await(long time, TimeUnit)等待指定时间
signal()唤醒一个等待线程
signalAll()唤醒所有等待线程

经典示例:生产者-消费者

java
public class BoundedBuffer<T> {

    private final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();

    private final T[] items;
    private int count, head, tail;

    @SuppressWarnings("unchecked")
    public BoundedBuffer(int capacity) {
        items = (T[]) new Object[capacity];
    }

    public void put(T item) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length) {
                notFull.await();  // 队列满,等「不满」
            }
            items[tail] = item;
            if (++tail == items.length) tail = 0;
            count++;
            notEmpty.signal();  // 通知「不空」
        } finally {
            lock.unlock();
        }
    }

    public T take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await();  // 队列空,等「非空」
            }
            T item = items[head];
            items[head] = null;  // 帮助 GC
            if (++head == items.length) head = 0;
            count--;
            notFull.signal();  // 通知「不满」
            return item;
        } finally {
            lock.unlock();
        }
    }
}

关键:生产者等 notFull,消费者等 notEmpty,互不干扰。

带超时的等待

java
lock.lock();
try {
    // 等 5 秒,不满足就放弃
    boolean success = condition.await(5, TimeUnit.SECONDS);
    if (success) {
        // 条件满足,执行任务
    } else {
        // 超时,执行其他逻辑
    }
} finally {
    lock.unlock();
}

awaitNanos:精确超时

java
lock.lock();
try {
    long nanosLeft = TimeUnit.SECONDS.toNanos(5);
    while (!conditionMet && nanosLeft > 0) {
        nanosLeft = condition.awaitNanos(nanosLeft);  // 返回剩余时间
    }
    if (conditionMet) {
        // 条件满足
    } else {
        // 超时
    }
} finally {
    lock.unlock();
}

awaitUninterruptibly:不响应中断

某些场景下,不希望被中断打断:

java
lock.lock();
try {
    condition.awaitUninterruptibly();  // 忽略中断,继续等
} finally {
    lock.unlock();
}

场景:后台任务,必须等待某个条件,除非程序退出。

signal vs signalAll

wait/notify 类似:

signal()signalAll()
唤醒数量一个所有
风险可能饥饿更安全
java
// 单个生产者/消费者,用 signal 就够
notEmpty.signal();  // 只唤醒一个消费者

// 多个生产者/消费者,用 signalAll 更安全
notEmpty.signalAll();  // 唤醒所有消费者

对比 wait/notify 的优势

1. 多个条件队列

java
// wait/notify:所有等待共用一个队列
synchronized (lock) {
    if (isFull()) wait();
    if (isEmpty()) wait();  // 和 isFull 混在一起
}

// Condition:每个条件独立队列
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();

if (isFull()) notFull.await();    // 只在这个队列等
if (isEmpty()) notEmpty.await();  // 在另一个队列等

2. 精确唤醒

java
// wait/notify:随机唤醒
lock.notify();  // 可能唤醒错的线程

// Condition:可以绑定到特定条件
notEmpty.signal();   // 只唤醒等「非空」的线程

3. 更丰富的超时控制

java
// wait:只能等待毫秒
lock.wait(5000);

// Condition:支持各种时间单位
condition.await(5, TimeUnit.SECONDS);
condition.await(5_000_000_000L, TimeUnit.NANOSECONDS);
condition.awaitUntil(deadline);

注意事项

1. 必须在锁内使用

java
// ❌ 错误
Condition condition = lock.newCondition();
condition.await();  // 抛异常

// ✅ 正确
lock.lock();
try {
    condition.await();
} finally {
    lock.unlock();
}

2. 永远在 finally 中释放锁

java
lock.lock();
try {
    while (!conditionMet) {
        condition.await();
    }
} finally {
    lock.unlock();  // 必须释放,否则死锁
}

3. signal 不立即交出锁

java
lock.lock();
try {
    condition.signal();  // 通知了,但还没释放锁
    // 这段代码继续执行
} finally {
    lock.unlock();  // 这里才释放锁
}

总结

  • Condition 可以创建多个条件队列,比 wait/notify 更灵活
  • await() 系列方法比 wait() 功能更丰富(支持超时、中断控制)
  • signal() 只唤醒一个,可能饥饿;signalAll() 唤醒所有,更安全
  • 必须在锁内使用,永远在 finally 中释放锁
  • 适合复杂的多线程协作场景

基于 VitePress 构建