概述
条件队列就像烤面包机中通知"面包已烤好"的铃声.如果注意听着铃声,那么当面包烤好后可以立刻得到通知,开始吃面包.如果没有听见铃声,那么会错过消息通知,但回到厨房时还可以观察烤面包机的状态,如果已经烤好,就取出面包;没有烤好就再次留意铃声
"条件队列"这个名字来源于:它使得一组线程(称之为等待线程集合)能够通过某种方式来等待特定的条件为真.传统队列的元素是一个个数据,而与之不同的是,条件队列中的元素是一个个正在等待相关条件的线程.
正如每个java对象都可以作为一个锁,每个对象同样可以作为一个条件队列,并且Object中的wait,notify和notifyAll方法就构成了内部条件队列的API.对象的内置锁与其内部条件队列是相互关联的,要调用对象X中的条件队列的任何一个方法,必须持有对象X上的锁.这是因为"等待由状态构成条件"与"维护状态一致性"这两种机制必须被紧密地绑定在一起:只有能对状态进行检查时,才能在某个条件上等待,并且只有能修改状态时,才能从条件等待中释放另一个线程.
Object.wait会自动释放锁,并请求操作系统挂起当前线程,从而使其他线程能够获得这个锁并修改对象的状态.当被挂起的线程醒来时,它将在返回之前重新获取锁.从直观上来理解,调用wait意味着"我要去休息了,但当发生特定的事情时唤醒我",而调用通知方法就意味着"特定事件发生了"
一个条件队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class BoundedBuffer<V> extends BaseBoundedBuffer<V> { public BoundedBuffer(int capacity) { super(capacity); }
public synchronized void put(V v) throws InterruptedException { while(isFull()){ wait(); } doPut(v); notifyAll(); }
public synchronized V take() throws InterruptedException { while(isEmpty()){ wait(); } V v = doTake(); notifyAll(); return v; } }
|
显式的Condition对象
一个Condition和一个Lock关联在一起.Condition比内置条件队列提供了更丰富的功能:
- 在每个锁上柯村仔多个等待
- 条件等待可以是可中断的或不可中断的
- 基于时限的等待
- 公平或非公平的队列操作
Condition对象会继承Lock对象的公平性
- 每个Lock都可以有任意数量的Condition对象
Condition的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| public class ConditionBoundedBuffer<T> { protected final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
@GuardedBy("lock") private int tail, head, count;
@GuardedBy("lock") private T[] items;
public BoundedBuffer(int capacity) { this.items = (T[]) new Object[capacity]; }
public void put(T t) throws InterruptedException { lock.lock(); try { while (count == items.length) { notFull.await(); } items[tail] = t; if (++tail == items.length) { tail = 0; } notEmpty.notify(); } finally { lock.unlock(); } }
public T take() throws InterruptedException { lock.lock(); try { while (count == 0) { notFull.await(); } T x = items[head]; items[head] = null; if (++head == items.length) { head = 0; } --count; notFull.signal(); return x; } finally { lock.unlock(); } } }
|
ConditionBoundedBudder的行为与BoundedBuffer相同,但它对条件队列的使用方式更容易理解.
通过将两个条件谓词分开并放到两个等待线程集中,Condition使其更容易满足单次通知的需求
signal比signalAll更高效.