Java多線程(六)之Deque與LinkedBlockingDeque深入分析

一、雙向隊列Deque


Queue除了前面介紹的實現外,還有一種雙向的Queue實現Deque。這種隊列允許在隊列頭和尾部進行入隊出隊操作,因此在功能上比Queue顯然要更復雜。下圖描述的是Deque的完整體系圖。需要說明的是LinkedList也已經加入了Deque的一部分(LinkedList是從jdk1.2 開始就存在數據結構)。

 


Deque在Queue的基礎上增加了更多的操作方法。


從上圖可以看到,Deque不僅具有FIFO的Queue實現,也有FILO的實現,也就是不僅可以實現隊列,也可以實現一個堆棧。

同時在Deque的體系結構圖中可以看到,實現一個Deque可以使用數組(ArrayDeque),同時也可以使用鏈表(LinkedList),還可以同實現一個支持阻塞的線程安全版本隊列LinkedBlockingDeque。


1、ArrayDeque實現Deque


對於數組實現的Deque來說,數據結構上比較簡單,只需要一個存儲數據的數組以及頭尾兩個索引即可。由於數組是固定長度的,所以很容易就得到數組的頭和尾,那麼對於數組的操作只需要移動頭和尾的索引即可。

特別說明的是ArrayDeque並不是一個固定大小的隊列,每次隊列滿了以後就將隊列容量擴大一倍(doubleCapacity()),因此加入一個元素總是能成功,而且也不會拋出一個異常。也就是說ArrayDeque是一個沒有容量限制的隊列。

同樣繼續性能的考慮,使用System.arraycopy複製一個數組比循環設置要高效得多。


1.1、ArrayDeque的源碼解析


[java] view plaincopy
  1. //數組雙端隊列ArrayDeque的源碼解析  
  2. public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>, Cloneable, Serializable{  
  3.     /** 
  4.      * 存放隊列元素的數組,數組的長度爲“2的指數” 
  5.      */  
  6.     private transient E[] elements;  
  7.     /** 
  8.      *隊列的頭部索引位置,(被remove()或pop()操作的位置),當爲空隊列時,首尾index相同 
  9.      */  
  10.     private transient int head;  
  11.     /** 
  12.      * 隊列的尾部索引位置,(被 addLast(E), add(E), 或 push(E)操作的位置). 
  13.      */  
  14.     private transient int tail;  
  15.     /** 
  16.      * 隊列的最小容量(大小必須爲“2的指數”) 
  17.      */  
  18.     private static final int MIN_INITIAL_CAPACITY = 8;  
  19.     // ******  Array allocation and resizing utilities ******  
  20.     /** 
  21.      * 根據所給的數組長度,得到一個比該長度大的最小的2^p的真實長度,並建立真實長度的空數組 
  22.      */  
  23.     private void allocateElements(int numElements) {  
  24.         int initialCapacity = MIN_INITIAL_CAPACITY;  
  25.         if (numElements >= initialCapacity) {  
  26.             initialCapacity = numElements;  
  27.             initialCapacity |= (initialCapacity >>>  1);  
  28.             initialCapacity |= (initialCapacity >>>  2);  
  29.             initialCapacity |= (initialCapacity >>>  4);  
  30.             initialCapacity |= (initialCapacity >>>  8);  
  31.             initialCapacity |= (initialCapacity >>> 16);  
  32.             initialCapacity++;  
  33.             if (initialCapacity < 0)   // Too many elements, must back off  
  34.                 initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements  
  35.         }  
  36.         elements = (E[]) new Object[initialCapacity];  
  37.     }  
  38.     /** 
  39.      * 當隊列首尾指向同一個引用時,擴充隊列的容量爲原來的兩倍,並對元素重新定位到新數組中 
  40.      */  
  41.     private void doubleCapacity() {  
  42.         assert head == tail;  
  43.         int p = head;  
  44.         int n = elements.length;  
  45.         int r = n - p; // number of elements to the right of p  
  46.         int newCapacity = n << 1;  
  47.         if (newCapacity < 0)  
  48.             throw new IllegalStateException("Sorry, deque too big");  
  49.         Object[] a = new Object[newCapacity];  
  50.         System.arraycopy(elements, p, a, 0, r);  
  51.         System.arraycopy(elements, 0, a, r, p);  
  52.         elements = (E[])a;  
  53.         head = 0;  
  54.         tail = n;  
  55.     }  
  56.     /** 
  57.      * 拷貝隊列中的元素到新數組中 
  58.      */  
  59.     private <T> T[] copyElements(T[] a) {  
  60.         if (head < tail) {  
  61.             System.arraycopy(elements, head, a, 0, size());  
  62.         } else if (head > tail) {  
  63.             int headPortionLen = elements.length - head;  
  64.             System.arraycopy(elements, head, a, 0, headPortionLen);  
  65.             System.arraycopy(elements, 0, a, headPortionLen, tail);  
  66.         }  
  67.         return a;  
  68.     }  
  69.     /** 
  70.      * 默認構造隊列,初始化一個長度爲16的數組 
  71.      */  
  72.     public ArrayDeque() {  
  73.         elements = (E[]) new Object[16];  
  74.     }  
  75.     /** 
  76.      * 指定元素個數的構造方法 
  77.      */  
  78.     public ArrayDeque(int numElements) {  
  79.         allocateElements(numElements);  
  80.     }  
  81.     /** 
  82.      * 用一個集合作爲參數的構造方法 
  83.      */  
  84.     public ArrayDeque(Collection<? extends E> c) {  
  85.         allocateElements(c.size());  
  86.         addAll(c);  
  87.     }  
  88.     //插入和刪除的方法主要是: addFirst(),addLast(), pollFirst(), pollLast()。  
  89.     //其他的方法依賴於這些實現。  
  90.     /** 
  91.      * 在雙端隊列的前端插入元素,元素爲null拋異常 
  92.      */  
  93.     public void addFirst(E e) {  
  94.         if (e == null)  
  95.             throw new NullPointerException();  
  96.         elements[head = (head - 1) & (elements.length - 1)] = e;  
  97.         if (head == tail)  
  98.             doubleCapacity();  
  99.     }  
  100.     /** 
  101.      *在雙端隊列的末端插入元素,元素爲null拋異常 
  102.      */  
  103.     public void addLast(E e) {  
  104.         if (e == null)  
  105.             throw new NullPointerException();  
  106.         elements[tail] = e;  
  107.         if ( (tail = (tail + 1) & (elements.length - 1)) == head)  
  108.             doubleCapacity();  
  109.     }  
  110.     /** 
  111.      * 在前端插入,調用addFirst實現,返回boolean類型 
  112.      */  
  113.     public boolean offerFirst(E e) {  
  114.         addFirst(e);  
  115.         return true;  
  116.     }  
  117.     /** 
  118.      * 在末端插入,調用addLast實現,返回boolean類型 
  119.      */  
  120.     public boolean offerLast(E e) {  
  121.         addLast(e);  
  122.         return true;  
  123.     }  
  124.     /** 
  125.      * 刪除前端,調用pollFirst實現 
  126.      */  
  127.     public E removeFirst() {  
  128.         E x = pollFirst();  
  129.         if (x == null)  
  130.             throw new NoSuchElementException();  
  131.         return x;  
  132.     }  
  133.     /** 
  134.      * 刪除後端,調用pollLast實現 
  135.      */  
  136.     public E removeLast() {  
  137.         E x = pollLast();  
  138.         if (x == null)  
  139.             throw new NoSuchElementException();  
  140.         return x;  
  141.     }  
  142.     //前端出對(刪除前端)  
  143.     public E pollFirst() {  
  144.         int h = head;  
  145.         E result = elements[h]; // Element is null if deque empty  
  146.         if (result == null)  
  147.             return null;  
  148.         elements[h] = null;     // Must null out slot  
  149.         head = (h + 1) & (elements.length - 1);  
  150.         return result;  
  151.     }  
  152.     //後端出對(刪除後端)  
  153.     public E pollLast() {  
  154.         int t = (tail - 1) & (elements.length - 1);  
  155.         E result = elements[t];  
  156.         if (result == null)  
  157.             return null;  
  158.         elements[t] = null;  
  159.         tail = t;  
  160.         return result;  
  161.     }  
  162.     /** 
  163.      * 得到前端頭元素 
  164.      */  
  165.     public E getFirst() {  
  166.         E x = elements[head];  
  167.         if (x == null)  
  168.             throw new NoSuchElementException();  
  169.         return x;  
  170.     }  
  171.     /** 
  172.      * 得到末端尾元素 
  173.      */  
  174.     public E getLast() {  
  175.         E x = elements[(tail - 1) & (elements.length - 1)];  
  176.         if (x == null)  
  177.             throw new NoSuchElementException();  
  178.         return x;  
  179.     }  
  180.     public E peekFirst() {  
  181.         return elements[head]; // elements[head] is null if deque empty  
  182.     }  
  183.     public E peekLast() {  
  184.         return elements[(tail - 1) & (elements.length - 1)];  
  185.     }  
  186.     /** 
  187.      * 移除此雙端隊列中第一次出現的指定元素(當從頭部到尾部遍歷雙端隊列時)。 
  188.      */  
  189.     public boolean removeFirstOccurrence(Object o) {  
  190.         if (o == null)  
  191.             return false;  
  192.         int mask = elements.length - 1;  
  193.         int i = head;  
  194.         E x;  
  195.         while ( (x = elements[i]) != null) {  
  196.             if (o.equals(x)) {  
  197.                 delete(i);  
  198.                 return true;  
  199.             }  
  200.             i = (i + 1) & mask;  
  201.         }  
  202.         return false;  
  203.     }  
  204.     /** 
  205.      * 移除此雙端隊列中最後一次出現的指定元素(當從頭部到尾部遍歷雙端隊列時)。 
  206.      */  
  207.     public boolean removeLastOccurrence(Object o) {  
  208.         if (o == null)  
  209.             return false;  
  210.         int mask = elements.length - 1;  
  211.         int i = (tail - 1) & mask;  
  212.         E x;  
  213.         while ( (x = elements[i]) != null) {  
  214.             if (o.equals(x)) {  
  215.                 delete(i);  
  216.                 return true;  
  217.             }  
  218.             i = (i - 1) & mask;  
  219.         }  
  220.         return false;  
  221.     }  
  222.     // *** 隊列方法(Queue methods) ***  
  223.     /** 
  224.      * add方法,添加到隊列末端 
  225.      */  
  226.     public boolean add(E e) {  
  227.         addLast(e);  
  228.         return true;  
  229.     }  
  230.     /** 
  231.      * 同上 
  232.      */  
  233.     public boolean offer(E e) {  
  234.         return offerLast(e);  
  235.     }  
  236.     /** 
  237.      * remove元素,刪除隊列前端 
  238.      */  
  239.     public E remove() {  
  240.         return removeFirst();  
  241.     }  
  242.     /** 
  243.      * 彈出前端(出對,刪除前端) 
  244.      */  
  245.     public E poll() {  
  246.         return pollFirst();  
  247.     }  
  248.     public E element() {  
  249.         return getFirst();  
  250.     }  
  251.     public E peek() {  
  252.         return peekFirst();  
  253.     }  
  254.     // *** 棧 方法(Stack methods) ***  
  255.     public void push(E e) {  
  256.         addFirst(e);  
  257.     }  
  258.     public E pop() {  
  259.         return removeFirst();  
  260.     }  
  261.     private void checkInvariants() { ……    }  
  262.     private boolean delete(int i) {   ……   }  
  263.     // *** 集合方法(Collection Methods) ***  
  264.     ……  
  265.     // *** Object methods ***  
  266.     ……  
  267. }  
  268. 整體來說:1個數組,2個index(head 索引和tail索引)。實現比較簡單,容易理解。  



2、LinkedList實現Deque



對於LinkedList本身而言,數據結構就更簡單了,除了一個size用來記錄大小外,只有head一個元素Entry。對比Map和Queue的其它數據結構可以看到這裏的Entry有兩個引用,是雙向的隊列。

在示意圖中,LinkedList總是有一個“傀儡”節點,用來描述隊列“頭部”,但是並不表示頭部元素,它是一個執行null的空節點。

隊列一開始只有head一個空元素,然後從尾部加入E1(add/addLast),head和E1之間建立雙向鏈接。然後繼續從尾部加入E2,E2就在head和E1之間建立雙向鏈接。最後從隊列的頭部加入E3(push/addFirst),於是E3就在E1和head之間鏈接雙向鏈接。

雙向鏈表的數據結構比較簡單,操作起來也比較容易,從事從“傀儡”節點開始,“傀儡”節點的下一個元素就是隊列的頭部,前一個元素是隊列的尾部,換句話說,“傀儡”節點在頭部和尾部之間建立了一個通道,是整個隊列形成一個循環,這樣就可以從任意一個節點的任意一個方向能遍歷完整的隊列。

同樣LinkedList也是一個沒有容量限制的隊列,因此入隊列(不管是從頭部還是尾部)總能成功。

 

3、小結 


上面描述的ArrayDeque和LinkedList是兩種不同方式的實現,通常在遍歷和節省內存上ArrayDeque更高效(索引更快,另外不需要Entry對象),但是在隊列擴容下LinkedList更靈活,因爲不需要複製原始的隊列,某些情況下可能更高效。

同樣需要注意的上述兩個實現都不是線程安全的,因此只適合在單線程環境下使用,下面章節要介紹的LinkedBlockingDeque就是線程安全的可阻塞的Deque。事實上也應該是功能最強大的Queue實現,當然了實現起來也許會複雜一點。


二、雙向併發阻塞隊列 LinkedBlockingDeque


1、LinkedBlockingDeque數據結構


雙向併發阻塞隊列。所謂雙向是指可以從隊列的頭和尾同時操作,併發只是線程安全的實現,阻塞允許在入隊出隊不滿足條件時掛起線程,這裏說的隊列是指支持FIFO/FILO實現的鏈表。

 

首先看下LinkedBlockingDeque的數據結構。通常情況下從數據結構上就能看出這種實現的優缺點,這樣就知道如何更好的使用工具了。


從數據結構和功能需求上可以得到以下結論:

  1. 要想支持阻塞功能,隊列的容量一定是固定的,否則無法在入隊的時候掛起線程。也就是capacity是final類型的。
  2. 既然是雙向鏈表,每一個結點就需要前後兩個引用,這樣才能將所有元素串聯起來,支持雙向遍歷。也即需要prev/next兩個引用。
  3. 雙向鏈表需要頭尾同時操作,所以需要first/last兩個節點,當然可以參考LinkedList那樣採用一個節點的雙向來完成,那樣實現起來就稍微麻煩點。
  4. 既然要支持阻塞功能,就需要鎖和條件變量來掛起線程。這裏使用一個鎖兩個條件變量來完成此功能。

2、LinkedBlockingDeque源碼分析


[java] view plaincopy
  1. public class LinkedBlockingDeque<E> extends AbstractQueue<E> implements BlockingDeque<E>,  java.io.Serializable {  
  2.     /** 包含前驅和後繼節點的雙向鏈式結構 */  
  3.     static final class Node<E> {  
  4.  E item;  
  5.         Node<E> prev;  
  6.         Node<E> next;  
  7.         Node(E x, Node<E> p, Node<E> n) {  
  8.             item = x;  
  9.             prev = p;  
  10.             next = n;  
  11.         }  
  12.     }  
  13.     /** 頭節點 */  
  14.     private transient Node<E> first;  
  15.     /** 尾節點 */  
  16.     private transient Node<E> last;  
  17.     /** 元素個數*/  
  18.     private transient int count;  
  19.     /** 隊列容量 */  
  20.     private final int capacity;  
  21.     /** 鎖 */  
  22.     private final ReentrantLock lock = new ReentrantLock();  
  23.     /** notEmpty條件 */  
  24.     private final Condition notEmpty = lock.newCondition();  
  25.     /** notFull條件 */  
  26.     private final Condition notFull = lock.newCondition();  
  27.     /** 構造方法 */  
  28.     public LinkedBlockingDeque() {  
  29.         this(Integer.MAX_VALUE);  
  30.     }  
  31.     public LinkedBlockingDeque(int capacity) {  
  32.         if (capacity <= 0throw new IllegalArgumentException();  
  33.         this.capacity = capacity;  
  34.     }  
  35.     public LinkedBlockingDeque(Collection<? extends E> c) {  
  36.         this(Integer.MAX_VALUE);  
  37.         for (E e : c)  
  38.             add(e);  
  39.     }  
  40.   
  41.     /** 
  42.      * 添加元素作爲新的頭節點 
  43.      */  
  44.     private boolean linkFirst(E e) {  
  45.         if (count >= capacity)  
  46.             return false;  
  47.         ++count;  
  48.         Node<E> f = first;  
  49.         Node<E> x = new Node<E>(e, null, f);  
  50.         first = x;  
  51.         if (last == null)  
  52.             last = x;  
  53.         else  
  54.             f.prev = x;  
  55.         notEmpty.signal();  
  56.         return true;  
  57.     }  
  58.     /** 
  59.      * 添加尾元素 
  60.      */  
  61.     private boolean linkLast(E e) {  
  62.         if (count >= capacity)  
  63.             return false;  
  64.         ++count;  
  65.         Node<E> l = last;  
  66.         Node<E> x = new Node<E>(e, l, null);  
  67.         last = x;  
  68.         if (first == null)  
  69.             first = x;  
  70.         else  
  71.             l.next = x;  
  72.         notEmpty.signal();  
  73.         return true;  
  74.     }  
  75.     /** 
  76.      * 返回並移除頭節點 
  77.      */  
  78.     private E unlinkFirst() {  
  79.         Node<E> f = first;  
  80.         if (f == null)  
  81.             return null;  
  82.         Node<E> n = f.next;  
  83.         first = n;  
  84.         if (n == null)  
  85.             last = null;  
  86.         else  
  87.             n.prev = null;  
  88.         --count;  
  89.         notFull.signal();  
  90.         return f.item;  
  91.     }  
  92.     /** 
  93.      * 返回並移除尾節點 
  94.      */  
  95.     private E unlinkLast() {  
  96.         Node<E> l = last;  
  97.         if (l == null)  
  98.             return null;  
  99.         Node<E> p = l.prev;  
  100.         last = p;  
  101.         if (p == null)  
  102.             first = null;  
  103.         else  
  104.             p.next = null;  
  105.         --count;  
  106.         notFull.signal();  
  107.         return l.item;  
  108.     }  
  109.     /** 
  110.      * 移除節點x 
  111.      */  
  112.     private void unlink(Node<E> x) {  
  113.         Node<E> p = x.prev;  
  114.         Node<E> n = x.next;  
  115.         if (p == null) {//x是頭的情況  
  116.             if (n == null)  
  117.                 first = last = null;  
  118.             else {  
  119.                 n.prev = null;  
  120.                 first = n;  
  121.             }  
  122.         } else if (n == null) {//x是尾的情況  
  123.             p.next = null;  
  124.             last = p;  
  125.         } else {//x是中間的情況  
  126.             p.next = n;  
  127.             n.prev = p;  
  128.         }  
  129.         --count;  
  130.         notFull.signalAll();  
  131.     }  
  132.     //--------------------------------- BlockingDeque 雙端阻塞隊列方法實現  
  133.     public void addFirst(E e) {  
  134.         if (!offerFirst(e))  
  135.             throw new IllegalStateException("Deque full");  
  136.     }  
  137.     public void addLast(E e) {  
  138.         if (!offerLast(e))  
  139.             throw new IllegalStateException("Deque full");  
  140.     }  
  141.     public boolean offerFirst(E e) {  
  142.         if (e == nullthrow new NullPointerException();  
  143.         lock.lock();  
  144.         try {  
  145.             return linkFirst(e);  
  146.         } finally {  
  147.             lock.unlock();  
  148.         }  
  149.     }  
  150.     public boolean offerLast(E e) {  
  151.         if (e == nullthrow new NullPointerException();  
  152.         lock.lock();  
  153.         try {  
  154.             return linkLast(e);  
  155.         } finally {  
  156.             lock.unlock();  
  157.         }  
  158.     }  
  159.     public void putFirst(E e) throws InterruptedException {  
  160.         if (e == nullthrow new NullPointerException();  
  161.         lock.lock();  
  162.         try {  
  163.             while (!linkFirst(e))  
  164.                 notFull.await();  
  165.         } finally {  
  166.             lock.unlock();  
  167.         }  
  168.     }  
  169.     public void putLast(E e) throws InterruptedException {  
  170.         if (e == nullthrow new NullPointerException();  
  171.         lock.lock();  
  172.         try {  
  173.             while (!linkLast(e))  
  174.                 notFull.await();  
  175.         } finally {  
  176.             lock.unlock();  
  177.         }  
  178.     }  
  179.     public boolean offerFirst(E e, long timeout, TimeUnit unit)  
  180.         throws InterruptedException {  
  181.         if (e == nullthrow new NullPointerException();  
  182.  long nanos = unit.toNanos(timeout);  
  183.         lock.lockInterruptibly();  
  184.         try {  
  185.             for (;;) {  
  186.                 if (linkFirst(e))  
  187.                     return true;  
  188.                 if (nanos <= 0)  
  189.                     return false;  
  190.                 nanos = notFull.awaitNanos(nanos);  
  191.             }  
  192.         } finally {  
  193.             lock.unlock();  
  194.         }  
  195.     }  
  196.     public boolean offerLast(E e, long timeout, TimeUnit unit)  
  197.         throws InterruptedException {  
  198.         if (e == nullthrow new NullPointerException();  
  199.  long nanos = unit.toNanos(timeout);  
  200.         lock.lockInterruptibly();  
  201.         try {  
  202.             for (;;) {  
  203.                 if (linkLast(e))  
  204.                     return true;  
  205.                 if (nanos <= 0)  
  206.                     return false;  
  207.                 nanos = notFull.awaitNanos(nanos);  
  208.             }  
  209.         } finally {  
  210.             lock.unlock();  
  211.         }  
  212.     }  
  213.     public E removeFirst() {  
  214.         E x = pollFirst();  
  215.         if (x == nullthrow new NoSuchElementException();  
  216.         return x;  
  217.     }  
  218.     public E removeLast() {  
  219.         E x = pollLast();  
  220.         if (x == nullthrow new NoSuchElementException();  
  221.         return x;  
  222.     }  
  223.     public E pollFirst() {  
  224.         lock.lock();  
  225.         try {  
  226.             return unlinkFirst();  
  227.         } finally {  
  228.             lock.unlock();  
  229.         }  
  230.     }  
  231.     public E pollLast() {  
  232.         lock.lock();  
  233.         try {  
  234.             return unlinkLast();  
  235.         } finally {  
  236.             lock.unlock();  
  237.         }  
  238.     }  
  239.     public E takeFirst() throws InterruptedException {  
  240.         lock.lock();  
  241.         try {  
  242.             E x;  
  243.             while ( (x = unlinkFirst()) == null)  
  244.                 notEmpty.await();  
  245.             return x;  
  246.         } finally {  
  247.             lock.unlock();  
  248.         }  
  249.     }  
  250.     public E takeLast() throws InterruptedException {  
  251.         lock.lock();  
  252.         try {  
  253.             E x;  
  254.             while ( (x = unlinkLast()) == null)  
  255.                 notEmpty.await();  
  256.             return x;  
  257.         } finally {  
  258.             lock.unlock();  
  259.         }  
  260.     }  
  261.     public E pollFirst(long timeout, TimeUnit unit)  
  262.         throws InterruptedException {  
  263.  long nanos = unit.toNanos(timeout);  
  264.         lock.lockInterruptibly();  
  265.         try {  
  266.             for (;;) {  
  267.                 E x = unlinkFirst();  
  268.                 if (x != null)  
  269.                     return x;  
  270.                 if (nanos <= 0)  
  271.                     return null;  
  272.                 nanos = notEmpty.awaitNanos(nanos);  
  273.             }  
  274.         } finally {  
  275.             lock.unlock();  
  276.         }  
  277.     }  
  278.     public E pollLast(long timeout, TimeUnit unit)  
  279.         throws InterruptedException {  
  280.  long nanos = unit.toNanos(timeout);  
  281.         lock.lockInterruptibly();  
  282.         try {  
  283.             for (;;) {  
  284.                 E x = unlinkLast();  
  285.                 if (x != null)  
  286.                     return x;  
  287.                 if (nanos <= 0)  
  288.                     return null;  
  289.                 nanos = notEmpty.awaitNanos(nanos);  
  290.             }  
  291.         } finally {  
  292.             lock.unlock();  
  293.         }  
  294.     }  
  295.     public E getFirst() {  
  296.         E x = peekFirst();  
  297.         if (x == nullthrow new NoSuchElementException();  
  298.         return x;  
  299.     }  
  300.     public E getLast() {  
  301.         E x = peekLast();  
  302.         if (x == nullthrow new NoSuchElementException();  
  303.         return x;  
  304.     }  
  305.     public E peekFirst() {  
  306.         lock.lock();  
  307.         try {  
  308.             return (first == null) ? null : first.item;  
  309.         } finally {  
  310.             lock.unlock();  
  311.         }  
  312.     }  
  313.     public E peekLast() {  
  314.         lock.lock();  
  315.         try {  
  316.             return (last == null) ? null : last.item;  
  317.         } finally {  
  318.             lock.unlock();  
  319.         }  
  320.     }  
  321.     public boolean removeFirstOccurrence(Object o) {  
  322.         if (o == nullreturn false;  
  323.         lock.lock();  
  324.         try {  
  325.             for (Node<E> p = first; p != null; p = p.next) {  
  326.                 if (o.equals(p.item)) {  
  327.                     unlink(p);  
  328.                     return true;  
  329.                 }  
  330.             }  
  331.             return false;  
  332.         } finally {  
  333.             lock.unlock();  
  334.         }  
  335.     }  
  336.     public boolean removeLastOccurrence(Object o) {  
  337.         if (o == nullreturn false;  
  338.         lock.lock();  
  339.         try {  
  340.             for (Node<E> p = last; p != null; p = p.prev) {  
  341.                 if (o.equals(p.item)) {  
  342.                     unlink(p);  
  343.                     return true;  
  344.                 }  
  345.             }  
  346.             return false;  
  347.         } finally {  
  348.             lock.unlock();  
  349.         }  
  350.     }  
  351.     //---------------------------------- BlockingQueue阻塞隊列 方法實現  
  352.     public boolean add(E e) {  
  353.  addLast(e);  
  354.  return true;  
  355.     }  
  356.     public boolean offer(E e) {  
  357.  return offerLast(e);  
  358.     }  
  359.     public void put(E e) throws InterruptedException {  
  360.  putLast(e);  
  361.     }  
  362.     public boolean offer(E e, long timeout, TimeUnit unit)  
  363.         throws InterruptedException {  
  364.  return offerLast(e, timeout, unit);  
  365.     }  
  366.     public E remove() {  
  367.  return removeFirst();  
  368.     }  
  369.     public E poll() {  
  370.  return pollFirst();  
  371.     }  
  372.     public E take() throws InterruptedException {  
  373.  return takeFirst();  
  374.     }  
  375.     public E poll(long timeout, TimeUnit unit) throws InterruptedException {  
  376.  return pollFirst(timeout, unit);  
  377.     }  
  378.     public E element() {  
  379.  return getFirst();  
  380.     }  
  381.     public E peek() {  
  382.  return peekFirst();  
  383.     }  
  384.     //------------------------------------------- Stack 方法實現  
  385.     public void push(E e) {  
  386.  addFirst(e);  
  387.     }  
  388.     public E pop() {  
  389.  return removeFirst();  
  390.     }  
  391.     //------------------------------------------- Collection 方法實現  
  392.     public boolean remove(Object o) {  
  393.  return removeFirstOccurrence(o);  
  394.     }  
  395.     public int size() {  
  396.         lock.lock();  
  397.         try {  
  398.             return count;  
  399.         } finally {  
  400.             lock.unlock();  
  401.         }  
  402.     }  
  403.     public boolean contains(Object o) {  
  404.         if (o == nullreturn false;  
  405.         lock.lock();  
  406.         try {  
  407.             for (Node<E> p = first; p != null; p = p.next)  
  408.                 if (o.equals(p.item))  
  409.                     return true;  
  410.             return false;  
  411.         } finally {  
  412.             lock.unlock();  
  413.         }  
  414.     }  
  415.     boolean removeNode(Node<E> e) {  
  416.         lock.lock();  
  417.         try {  
  418.             for (Node<E> p = first; p != null; p = p.next) {  
  419.                 if (p == e) {  
  420.                     unlink(p);  
  421.                     return true;  
  422.                 }  
  423.             }  
  424.             return false;  
  425.         } finally {  
  426.             lock.unlock();  
  427.         }  
  428.     }  
  429.   ……  
  430. }  


3、LinkedBlockingDeque的優缺點


有了上面的結論再來研究LinkedBlockingDeque的優缺點。

優點當然是功能足夠強大,同時由於採用一個獨佔鎖,因此實現起來也比較簡單。所有對隊列的操作都加鎖就可以完成。同時獨佔鎖也能夠很好的支持雙向阻塞的特性。

凡事有利必有弊。缺點就是由於獨佔鎖,所以不能同時進行兩個操作,這樣性能上就大打折扣。從性能的角度講LinkedBlockingDeque要比LinkedBlockingQueue要低很多,比CocurrentLinkedQueue就低更多了,這在高併發情況下就比較明顯了。

前面分析足夠多的Queue實現後,LinkedBlockingDeque的原理和實現就不值得一提了,無非是在獨佔鎖下對一個鏈表的普通操作。


4、LinkedBlockingDeque的序列化、反序列化


有趣的是此類支持序列化,但是Node並不支持序列化,因此fist/last就不能序列化,那麼如何完成序列化/反序列化過程呢?

清單4 LinkedBlockingDeque的序列化、反序列化

[java] view plaincopy
  1. <span style="font-size:14px;">private void writeObject(java.io.ObjectOutputStream s)  
  2.     throws java.io.IOException {  
  3.     lock.lock();  
  4.     try {  
  5.         // Write out capacity and any hidden stuff  
  6.         s.defaultWriteObject();  
  7.         // Write out all elements in the proper order.  
  8.         for (Node<E> p = first; p != null; p = p.next)  
  9.             s.writeObject(p.item);  
  10.         // Use trailing null as sentinel  
  11.         s.writeObject(null);  
  12.     } finally {  
  13.         lock.unlock();  
  14.     }  
  15. }  
  16.   
  17. private void readObject(java.io.ObjectInputStream s)  
  18.     throws java.io.IOException, ClassNotFoundException {  
  19.     s.defaultReadObject();  
  20.     count = 0;  
  21.     first = null;  
  22.     last = null;  
  23.     // Read in all elements and place in queue  
  24.     for (;;) {  
  25.         E item = (E)s.readObject();  
  26.         if (item == null)  
  27.             break;  
  28.         add(item);  
  29.     }  
  30. }  
  31.   
  32.  </span>  


清單4 描述的是LinkedBlockingDeque序列化/反序列化的過程。序列化時將真正的元素寫入輸出流,最後還寫入了一個null。讀取的時候將所有對象列表讀出來,如果讀取到一個null就表示結束。這就是爲什麼寫入的時候寫入一個null的原因,因爲沒有將count寫入流,所以就靠null來表示結束,省一個整數空間。


參考內容來源:

集合框架 Queue篇(1)---ArrayDeque
http://hi.baidu.com/yao1111yao/item/1a1346f65a50d9c8521c266d
集合框架 Queue篇(7)---LinkedBlockingDeque
http://hi.baidu.com/yao1111yao/item/b1649cff2cf60be91a111f6d
深入淺出 Java Concurrency (24): 併發容器 part 9 雙向隊列集合 Deque
http://www.blogjava.net/xylz/archive/2010/08/12/328587.html
深入淺出 Java Concurrency (25): 併發容器 part 10 雙向併發阻塞隊列 BlockingDeque
http://www.blogjava.net/xylz/archive/2010/08/18/329227.html
發佈了41 篇原創文章 · 獲贊 22 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章