在Java中,Condition
接口是用于线程间通信的高级工具,它提供了比传统Object
类的wait()
和notify()
方法更强大的功能和灵活性。Condition
接口位于java.util.concurrent.locks
包中,通常与Lock
接口的实现如ReentrantLock
结合使用。下面我们将探讨Condition
的原理以及它的使用场景。
Condition的原理
Condition
接口的设计目的是为了解决Object
类的wait()
和notify()
方法的一些限制和不足,比如:
- 单一等待队列:
Object
的wait()
方法使得所有等待的线程都在同一个等待队列中,而Condition
允许为不同的条件创建多个等待队列,这样可以更精细地控制哪些线程被唤醒。 - 精确唤醒:
Condition
的signal()
方法可以唤醒一个等待在该条件上的线程,而signalAll()
方法可以唤醒所有等待的线程,这比Object
的notify()
和notifyAll()
更可控。 - 异常处理:
Condition
的await()
方法会在释放锁的情况下等待,如果线程被中断,await()
会抛出InterruptedException
并清除中断状态,而Object
的wait()
方法在被中断时不会清除中断状态。
Condition
的内部实现基于AbstractQueuedSynchronizer
(AQS),AQS提供了线程同步的基础框架。Condition
的等待队列与AQS的同步队列是分离的,这意味着当线程调用await()
方法时,它会被移动到与Condition
关联的等待队列中,而不是同步队列。当线程被唤醒时,它会被移回到同步队列中,以便重新竞争锁。
使用场景
Condition
接口适用于需要更复杂线程间协作的场景,例如:
- 生产者-消费者模式:在多生产者多消费者模式中,可以为生产者和消费者分别创建不同的
Condition
对象,这样生产者可以等待队列空间可用,消费者可以等待队列中有数据。这比使用单一的wait()
和notifyAll()
方法更有效率和准确。 - 多条件同步:在某些情况下,线程的执行可能依赖于多个条件,例如一个任务可能需要等待两个独立的事件同时发生。在这种情况下,可以为每个条件创建一个
Condition
对象,从而实现更精确的线程唤醒控制。 - 超时等待:
Condition
接口提供了带超时的await()
方法,允许线程在指定时间内等待条件发生,超时后自动恢复执行,这对于实现限时等待非常有用。
示例代码
下面是一个简单的使用Condition
的例子,演示了如何在生产者-消费者模式中使用Condition
:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Buffer {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final Object[] items = new Object[10];
private int putIndex, takeIndex, count;
public void put(Object item) throws InterruptedException {
lock.lock();
try {
while (count == items.length) {
notFull.await();
}
items[putIndex] = item;
if (++putIndex == items.length) putIndex = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
notEmpty.await();
}
Object x = items[takeIndex];
if (++takeIndex == items.length) takeIndex = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
在这个例子中,Buffer
类使用ReentrantLock
和两个Condition
对象notFull
和notEmpty
来控制生产者和消费者的线程同步。当缓冲区满时,生产者会等待notFull
条件;当缓冲区空时,消费者会等待notEmpty
条件。当条件满足时,相应的Condition
对象会通过signal()
方法唤醒等待的线程。