Java多线程(五)之BlockingQueue深入分析(三)

2014-11-24 10:44:01 · 作者: · 浏览: 3
护这个队列中的数据变更:
/** items index for next take, poll or remove */
private int takeIndex;
/** items index for next put, offer, or add. */
private int putIndex;
/** Number of items in the queue */
private int count;

提取元素的三个方法take/poll/remove内部都调用了这个方法:

Java代码


[java]
private E extract() {
final E[] items = this.items;
E x = items[takeIndex];
items[takeIndex] = null;//移除已经被提取出的元素
takeIndex = inc(takeIndex);//策略和添加元素时相同
--count;
notFull.signal();//提醒其他在notFull这个Condition上waiting的线程可以尝试工作了
return x;
}
从这个方法里可见,tabkeIndex维护一个可以提取/移除元素的索引位置,因为takeIndex是从0递增的,所以这个类是FIFO队列。

putIndex维护一个可以插入的元素的位置索引。

count显然是维护队列中已经存在的元素总数。

1.3 Condition实现

Condition现在的实现只有java.util.concurrent.locks.AbstractQueueSynchoronizer内部的ConditionObject,并且通过ReentranLock的newCondition()方法暴露出来,这是因为Condition的await()/sinal()一般在lock.lock()与lock.unlock()之间执行,当执行condition.await()方法时,它会首先释放掉本线程持有的锁,然后自己进入等待队列。直到sinal(),唤醒后又会重新试图去拿到锁,拿到后执行await()下的代码,其中释放当前锁和得到当前锁都需要ReentranLock的tryAcquire(int arg)方法来判定,并且享受ReentranLock的重进入特性。

Java代码


[java]
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//加一个新的condition等待节点
Node node = addConditionWaiter();
//释放自己的锁
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
//如果当前线程 等待状态时CONDITION,park住当前线程,等待condition的signal来解除
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}

2、SynchronousQueue

一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然。同步队列没有任何内部容量,甚至连一个队列的容量都没有。不能在同步队列上进行 peek,因为仅在试图要取得元素时,该元素才存在;除非另一个线程试图移除某个元素,否则也不能(使用任何方法)添加元素;也不能迭代队列,因为其中没有元素可用于迭代。队列的头 是尝试添加到队列中的首个已排队线程元素;如果没有已排队线程,则不添加元素并且头为 null。对于其他Collection 方法(例如 contains),SynchronousQueue 作为一个空集合。此队列不允许 null 元素。

同步队列类似于 CSP 和 Ada 中使用的 rendezvous 信道。它非常适合于传递性设计,在这种设计中,在一个线程中运行的对象要将某些信息、事件或任务传递给在另一个线程中运行的对象,它就必须与该对象同步。

对于正在等待的生产者和使用者线程而言,此类支持可选的公平排序策略。默认情况下不保证这种排序。但是,使用公平设置为 true 所构造的队列可保证线程以 FIFO 的顺序进行访问。公平通常会降低吞吐量,但是可以减小可变性并避免得不到服务。


3、LinkedBlockingQueue

一个基于已链接节点的、范围任意的 blocking queue。此队列按 FIFO(先进先出)排序元素。队列的头部 是在队列中时间最长的元素。队列的尾部 是在队列中时间最短的元素。新元素插入到队列的尾部,并且队列检索操作会获得位于队列头部的元素。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。
单向链表结构的队列。如果不指定容量默认为Integer.MAX_VALUE。通过putLock和takeLock两个锁进行同步,两个锁分别实例化notFull和notEmpty两个Condtion,用来协调多线程的存取动作。其中某些方法(如remove,toArray,toString,clear等)的同步需要同时获得这两个锁,并且总是先putLock.lock紧接着takeLock.lock(在同一方法fullyLock中),这样的顺序是为了避免可能出现的死锁情况(我也想不明白为什么会是这样 )

4、PriorityBlockingQue