0%

Java[并发]-LinkedBlockingDeque实现分析

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;
/** deque中的元素数量 */
private transient int count;
/** deque的容量 */
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 {
//添加到队列头部失败,释放锁,进入Condition等待队列
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 {
//添加到队列尾部部失败,释放锁,进入Condition等待队列
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框架。