系列傳送門:
- Java併發包原始碼學習系列:AbstractQueuedSynchronizer
- Java併發包原始碼學習系列:CLH同步佇列及同步資源獲取與釋放
- Java併發包原始碼學習系列:AQS共享式與獨佔式獲取與釋放資源的區別
- Java併發包原始碼學習系列:ReentrantLock可重入獨佔鎖詳解
- Java併發包原始碼學習系列:ReentrantReadWriteLock讀寫鎖解析
- Java併發包原始碼學習系列:詳解Condition條件佇列、signal和await
- Java併發包原始碼學習系列:掛起與喚醒執行緒LockSupport工具類
- Java併發包原始碼學習系列:JDK1.8的ConcurrentHashMap原始碼解析
- Java併發包原始碼學習系列:阻塞佇列BlockingQueue及實現原理分析
- Java併發包原始碼學習系列:阻塞佇列實現之ArrayBlockingQueue原始碼解析
- Java併發包原始碼學習系列:阻塞佇列實現之LinkedBlockingQueue原始碼解析
- Java併發包原始碼學習系列:阻塞佇列實現之PriorityBlockingQueue原始碼解析
- Java併發包原始碼學習系列:阻塞佇列實現之DelayQueue原始碼解析
- Java併發包原始碼學習系列:阻塞佇列實現之SynchronousQueue原始碼解析
- Java併發包原始碼學習系列:阻塞佇列實現之LinkedTransferQueue原始碼解析
LinkedBlockingDeque概述
LinkedBlockingDeque是由連結串列構成的界限可選的雙端阻塞佇列,支援O(1)的時間複雜度從兩端插入和移除元素,如不指定邊界,則為Integer.MAX_VALUE
。
由一個ReentrantLock保證同步,使用conditions來實現等待通知。
類圖結構及重要欄位
public class LinkedBlockingDeque<E>
extends AbstractQueue<E>
implements BlockingDeque<E>, java.io.Serializable {
private static final long serialVersionUID = -387911632671998426L;
/** 雙向連結串列節點 */
static final class Node<E> {
E item;
Node<E> prev;
Node<E> next;
Node(E x) {
item = x;
}
}
/**
* 指向第一個節點
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* 指向最後一個節點
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
/** 節點數量 */
private transient int count;
/** 佇列容量 */
private final int capacity;
/** 保證同步 */
final ReentrantLock lock = new ReentrantLock();
/** take操作發生的條件 */
private final Condition notEmpty = lock.newCondition();
/** put操作發生的條件 */
private final Condition notFull = lock.newCondition();
}
linkFirst
嘗試將節點加入到first之前,更新first,如果插入之後超出容量,返回false。
private boolean linkFirst(Node<E> node) {
// assert lock.isHeldByCurrentThread();
if (count >= capacity)
return false;
Node<E> f = first;
node.next = f;
first = node;
if (last == null)
last = node;
else
f.prev = node;
++count;
notEmpty.signal();
return true;
}
linkLast
在last節點後加入節點node,更新last。如果插入之後超出容量,返回false。
private boolean linkLast(Node<E> node) {
// assert lock.isHeldByCurrentThread();
if (count >= capacity)
return false;
Node<E> l = last;
node.prev = l;
last = node;
if (first == null)
first = node;
else
l.next = node;
++count;
notEmpty.signal();// 滿足notEmpty條件
return true;
}
unlinkFirst
移除first節點,並返回其item值,如果佇列為空,則返回full。
private E unlinkFirst() {
// assert lock.isHeldByCurrentThread();
Node<E> f = first;
if (f == null)
return null;
Node<E> n = f.next;
E item = f.item;
f.item = null;
f.next = f; // help GC
first = n;
if (n == null)
last = null;
else
n.prev = null;
--count;
notFull.signal();// 滿足notFull條件
return item;
}
unlinkLast
移除last節點,並返回其item值,如果佇列為空,則返回full。
private E unlinkLast() {
// assert lock.isHeldByCurrentThread();
Node<E> l = last;
if (l == null)
return null;
Node<E> p = l.prev;
E item = l.item;
l.item = null;
l.prev = l; // help GC
last = p;
if (p == null)
first = null;
else
p.next = null;
--count;
notFull.signal(); // 滿足notFull條件
return item;
}
unlink
移除任意一個節點,注意這裡並沒有操作x本身的連線,因為它可能仍被iterator使用著。
void unlink(Node<E> x) {
// assert lock.isHeldByCurrentThread();
Node<E> p = x.prev;
Node<E> n = x.next;
// 移除的是first
if (p == null) {
unlinkFirst();
// 移除的是last
} else if (n == null) {
unlinkLast();
} else {
// 移除的是中間節點
p.next = n;
n.prev = p;
x.item = null;
// Don't mess with x's links. They may still be in use by
// an iterator.
// 這裡x的prev和next指標都沒有改變,因為他們可能在被iterator使用
--count;
notFull.signal();
}
}
總結
LinkedBlockingDeque是由連結串列構成的界限可選的雙端阻塞佇列,支援O(1)的時間複雜度從兩端插入和移除元素,如不指定邊界,則為Integer.MAX_VALUE
。
由一個ReentrantLock保證同步,使用conditions來實現等待通知。
上面介紹的所有操作基本上就是核心方法啦,諸如putFirst、putLast、takeFirst、takeLast等方法都會呼叫上面的核心方法,而且實現上面也是比較簡單的,就是雙端連結串列的基本操作,不懂的可以畫畫圖幫助理解哈。
參考閱讀
-
《Java併發程式設計的藝術》
-
《Java併發程式設計之美》