深刻淺出 Java Concurrency (25): 併發容器 part 10 雙向併發阻塞隊列 BlockingDeque[轉]

這個小節介紹Queue的最後一個工具,也是最強大的一個工具。從名稱上就能夠看到此工具的特色:雙向併發阻塞隊列。所謂雙向是指能夠從隊列的頭和尾同時操做,併發只是線程安全的實現,阻塞容許在入隊出隊不知足條件時掛起線程,這裏說的隊列是指支持FIFO/FILO實現的鏈表。java

 

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

LinkedBlockingDeque類圖

從數據結構和功能需求上能夠獲得如下結論:數據結構

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

 

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

優勢固然是功能足夠強大,同時因爲採用一個獨佔鎖,所以實現起來也比較簡單。全部對隊列的操做都加鎖就能夠完成。同時獨佔鎖也可以很好的支持雙向阻塞的特性。高併發

凡事有利必有弊。缺點就是因爲獨佔鎖,因此不能同時進行兩個操做,這樣性能上就大打折扣。從性能的角度講LinkedBlockingDeque要比LinkedBlockingQueue要低不少,比CocurrentLinkedQueue就低更多了,這在高併發狀況下就比較明顯了。工具

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

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

清單1 LinkedBlockingDeque的序列化、反序列化線程

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    lock.lock();
    try {
        // Write out capacity and any hidden stuff
        s.defaultWriteObject();
        // Write out all elements in the proper order.
        for (Node<E> p = first; p != null; p = p.next)
            s.writeObject(p.item);
        // Use trailing null as sentinel
        s.writeObject(null);
    } finally {
        lock.unlock();
    }
}對象

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();
    count = 0;
    first = null;
    last = null;
    // Read in all elements and place in queue
    for (;;) {
        E item = (E)s.readObject();
        if (item == null)
            break;
        add(item);
    }
}

 

清單1 描述的是LinkedBlockingDeque序列化/反序列化的過程。序列化時將真正的元素寫入輸出流,最後還寫入了一個null。讀取的時候將全部對象列表讀出來,若是讀取到一個null就表示結束。這就是爲何寫入的時候寫入一個null的緣由,由於沒有將count寫入流,因此就靠null來表示結束,省一個整數空間。

相關文章
相關標籤/搜索