LinkedBlockingDeque是由链表构成的双向队列,可以从队列的两端插入和删除元素。双向队列多了一个端的操作入口,所以在并发时,可以减少一半的竞争。LinkedBlockingDeque比起LinkedBlockingQueue多了操作另一端的方法,所以它的方法offer/put/poll/take是成对的,变成了offerFirst/offerLast/pollFirst/pollLast等。LinkedBlockingDeque可以初始化时指定容量,如果不指定,则为Integer.MAX_VALUE。
LinkedBlockingDeque的并发控制同样使用非公平锁版本的ReentrantLock和两个Condition对象notFull和notEmpty来实现。
一、LinkedBlockingDeque的结构
LinkedBlockingDeque的结构由一个内部类Node表示队列中的节点,Node中有prev,next两个引用分别指向前后节点。LinkedBlockingDeque分别由first、last分别代表队列的首结点和尾节点。还有int型的count和capacity代表队列中的元素数量和队列的容量。相关代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| static final class Node<E> { E item; Node<E> prev; Node<E> next; Node(E x, Node<E> p, Node<E> n) { item = x; prev = p; next = n; } } transient Node<E> first; transient Node<E> last; private transient int count; private final int capacity; final ReentrantLock lock = new ReentrantLock(); private final Condition notEmpty = lock.newCondition(); private final Condition notFull = lock.newCondition();
|
二、LinkedBlockingDeque的主要方法实现
LinkedBlockingDeque主要的入队和出队方法都是基于linkFirst/linkLast/unlinkFirst/unlinkLast来实现的。
1、入队操作
入队操作的方法是成对的,有putFirst/putLast/offerFirst/offerLast等。代码实现比较简单:
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 void putFirst(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); final ReentrantLock lock = this.lock; lock.lock(); try { while (!linkFirst(e)) notFull.await(); } finally { lock.unlock(); } } public void putLast(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); final ReentrantLock lock = this.lock; lock.lock(); try { while (!linkLast(e)) notFull.await(); } finally { lock.unlock(); } }
|
2、出队操作
同样,出队操作也是成对的方法pollFirst/pollLast/takeFirst/takeLast.
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 E takeFirst() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lock(); try { E x; while ( (x = unlinkFirst()) == null) notEmpty.await(); return x; } finally { lock.unlock(); } } public E takeLast() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lock(); try { E x; while ( (x = unlinkLast()) == null) notEmpty.await(); return x; } finally { lock.unlock(); } }
|
三、总结
LinkedBlockingDeque的功能很强大,但使用的是一个独占锁,两头的操作其实都是用同一个锁,所以并发能力理论上不会太高。LinkedBlockingDeque一般运用在工作窃取的场景中,如Fork/Join框架。