并发编程--Condition
任意一个java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait(),wait(long timeout)、notify()以及notifyAll()方法,他们与synchronized关键字配合,可以实现等待/通知模式。Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知知模式,但是这两者在使用方式以及功能特性上还是有差别的。
一、Condition接口
Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创建出来的,换句话说,Condition是依赖Lock对象的。
主要方法包括:
1、void await() throws InterruptedException
当前线程进入等待状态直到被通知(signal)或中断。
2、void awaitUninterruptibly()
当前线程进入等待状态直到被通知,对中断不敏感。
3、long awaitNanos(long nanosTimeout) throws InterruptedException
当前线程进入等待状态直到被通知、中断或超时。
4、boolean awaitUntil(Date deadline) throws InterruptedException
当前线程进入等待状态直到被通知、中断或到某个时间。
5、signal()
唤醒一个等待在Condition上的线程,该线程从等待方法返回前必须获得与condition对象对应的锁。
6、signalAll()
唤醒所有等待在condition上的线程,能够从等待方法返回的线程必须获得与condition对象相关联的锁。
二、使用condition实现一个有界队列
有界队列是一种特殊的队列,当队列为空时,队列的获取操作将会阻塞获取线程,直到队列中有新增元素,当队列已满时,队列的插入操作将会阻塞插入线程,直到队列出现“空位”。
public class BoundQueue<T> {
private Lock lock = new ReentrantLock();
//数组添加的下标、删除的下标和当前的数量
private int addIndex , removeIndex , count;
private Object[] items;
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public BoundQueue(int size){
items = new Object[size];
}
//添加元素
public final void add(T t) throws InterruptedException{
lock.lock();
try{
while(count==items.length){
notFull.await();
}
items[addIndex] = t;
if(++addIndex==items.length){
addIndex = 0;
}
count ++;
notEmpty.signal();
}finally{
lock.unlock();
}
}
//获取元素
public final T remove() throws InterruptedException{
lock.lock();
try{
while(count==0){
notEmpty.await();
}
Object object = items[removeIndex];
if(++removeIndex == items.length){
removeIndex = 0;
}
count --;
return (T)object;
}finally{
lock.unlock();
}
}
}
首先需要获得锁,目的是确保数组修改的可见性和排他性。当数组数量等于数组长度时,表示数组已满,则调用notFull.await(),当前线程随之释放锁并进入等待状态。如果数组数量不等于数组长度,表示数组未满,则添加元素到数组中,同时通知等待在notEmpty上的线程,数组中已经有新元素可以获取。在添加和删除方法中使用while循环而非if判断,目的是防止过早或意外的通知,只有条件符合才能够退出循环。回想之前提到的等待/通知的经典范式,二者是非常类似的。
三、 Condition的实现
ConditionObject是同步器AbstractQueuedSynchronizer的内部类,因为Condition的操作需要获取相关联的锁,所以作为同步器的内部类也较为合理。每个Condition对象都包含着一个队列(以下称为等待队列),该队列是Condition对象实现等待/通知功能的关键。
当线程调用等待方法,该线程会被构造成等待节点并加入到等待队列中。
当线程调用通知方法会将等待队列中的首节点或者全部节点(notifyAll)移到同步队列中,然后唤醒相应的节点加入到同步状态的获取中(自旋获取)。当节点中的线程获取到了同步状态即锁时,会从等待方法返回。