Condition接口
概述
condition与Lock的实现类结合使用。 如果Lock替换了synchronized方法和语句的使用,则Condition将替换Object监视方法(wait,notify和notifyAll)的使用。
condition,也称为condition queue或者condition variables,能让一个线程阻塞在条件变量上,直到其他线程通知该线程条件变量现在可能为true。当一个线程等待条件时,它会自动释放相关联的锁,并陷入阻塞状态,就跟Object.wait()方法一样。
一个Condition实例一个Lock实例绑定,要获取特定Lock实例的Condition实例,可以调用Lock实例的newCondition()方法。
以生产者/消费者模式为例,假设我们有一个有界缓冲区,它支持put和take方法。 如果缓冲区为空,则消费线程将阻塞,直到缓冲区有内容为止; 如果缓冲区已满,则生产线程阻塞,直到缓冲区有空间可用为止。 我们希望把put线程和take线程放在不同的waitset中,以便我们在缓冲区中的内容可用或空间可用时,实现只通知一个线程。 这可以使用两个Condition实例来实现。
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
Condition实现类提供与Object监视器方法不同的行为和语义,例如在waitset中阻塞等待时可以不响应中断,多个waitset。
在等待条件时,虚假唤醒是可以发生的。因此应用程序的程序员应该编写循环,在循环中等待条件。
等待条件有以下三种形式:
1、不可中断:void awaitUninterruptibly();
2、可中断:void await() throws InterruptedException;
3、定时: long awaitNanos(long nanosTimeout) throws InterruptedException; 返回值表示当前剩余的时间。
await()方法
await方法使当前线程阻塞等待,直到被通知或者被中断。
与该condition关联的lock会被自动释放,并且由于线程调度的原因线程变得不可用,直到以下情形之一发生:
1、其他线程调用了这个condition的signal()方法,并且当前线程被选为唤醒的线程;
2、其他线程调用了这个condition的signalAll()方法;
3、其他线程调用当前线程的Thread.interrupt()方法;
4、一个虚假唤醒发生时;
以上所有情形中,当前线程都必须重新获得与该condition关联的lock,才能从await方法返回。
如果当前线程在阻塞等待时被中断,将会抛出 InterruptedException,并清除中断状态。
signal()方法
唤醒等待线程。
如果存在线程在条件上等待,选择其中一个作为被唤醒的线程。
在调用该方法时,要求当前线程持有与condition关联的Lock。否则抛出IllegalMonitorStateException。
ConditionObject
ConditionObject是Condition在java并发中的具体实现,它是AQS的内部类。因为Condition相关操作都需要获取锁,所以作为AQS的内部类很合理。