ArrayBlockingQueue與LinkedBlockingQueue對比

ps:在下文中分別以Array表明ArrayBlockingQueue,Link表明LinkedBlockingQueue,下文中再也不說明。java

Array和Link在併發場景中常用,他們的共同做用就是實現線程安全隊列。下面對這兩種隊列的實現進行對比分析。數組

底層實現

ArrayBlockingQueue

  • 底層基於數組實現,在對象建立時須要指定數組大小。在構建對象時,已經建立了數組。因此使用Array須要特別注意設定合適的隊列大小,若是設置過大會形成內存浪費。若是設置內存過小,就會影響併發的性能。
  • 功能上,其內部維護了兩個索引指針putIndex和takeIndex。putIndex表示下次調用offer時存放元素的位置,takeIndex表示的時下次調用take時獲取的元素。有了這兩個索引的支持後,仍是沒法說明白其底層的實現原理。那麼咱們來看一段其內部出現最多的代碼:
int i = takeIndex;
    ...
    if (++i == items.length)
        i = 0;
    ...

這幾行在代碼在Array中幾乎每一個函數都會用到。意思不論是在讀取元素,或者存放元素,若是到達數組的最後一個元素,直接將索引移動到第一個位置。你可能會想,若是我一直往隊列中添加元素而不取,添加的元素個數超過了數組長度,會不會覆蓋以前添加的元素。在實際使用過程當中是不會出現這種狀況的,其內部使用了ReentrantLock的Condition,這部分在併發支持中介紹。安全

LinkedBlockingQueue

  • 底層基於單向鏈表實現。實現了隊列的功能,元素到來放到鏈表頭,從鏈表尾部取取數據。這種數據結構沒有必要使用雙向鏈表。鏈表的好處(數組的沒有的)是不用提早分配內存。Link也支持在建立對象時指定隊列長度,若是沒有指定,默認爲Integer.MAX_VALUE。

併發支持

最大的區別就是Array內部只有一把鎖,offer和take使用同一把鎖,而Link的offer和take使用不一樣的鎖。數據結構

ReentrantLock和其Condition的關係

在作具體分析以前,先介紹一下ReentrantLock 和其Condition之間的關係。ReentrantLock內部維護了一個雙向鏈表,鏈表上的每一個節點都會保存一個線程,鎖在雙向鏈表的頭部自選,取出線程執行。而Condition內部一樣維持着一個雙向鏈表,可是其向鏈表中添加元素(await)和從鏈表中移除(signal)元素沒有像ReentrantLock那樣,保證線程安全,因此在調用Condition的await()和signal()方法時,須要在lock.lock()和lock.unlock()之間以保證線程的安全。在調用Condition的signal時,它從本身的雙向鏈表中取出一個節點放到了ReentrantLock的雙向鏈表中,因此在具體的運行過程當中無論ReentrantLock new 了幾個Condition其實內部公用的一把鎖。介紹完這個以後,我麼來分析ArrayBlockingQueue和LinkedBlockingQueue的內部實現不一樣。併發

ArrayBlockingQueue

先看其內部鎖的定義:函數

int count;
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
  • lock 其內部的鎖
  • notEmpty 當調用offer時,會調用notEmpty.signal() 通知以前由於隊列空而被阻塞的線程。同時在take後,若是內部計數器count=0時,會調用notEmpty.await() 阻塞調用take的線程。
  • notFull 當調用offer時,若是如今count=內部數組的長度時,會調用notFull.await()阻塞如今添加元素的全部線程;當調用take時,總會調用notFull.signal()喚醒以前由於隊列滿而阻塞的線程。

根據上面分析ReentrantLock和其Condition的關係,能夠看到放元素和取元素用的同一把鎖,沒法使放元素和取元素同時進行,只能前後相繼執行。性能

LinkedBlockingQueue

內部鎖定義:線程

/** Current number of elements */
    private final AtomicInteger count = new AtomicInteger();

    /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

    /** Lock held by put, offer, etc */
    private final ReentrantLock putLock = new ReentrantLock();

    /** Wait queue for waiting puts */
    private final Condition notFull = putLock.newCondition();
  • count 內部元素計數器使用的原子類型的計數器,使的元素個數的更新支持併發,爲下面取和放元素併發提供了支持。
  • takeLock 取元素單獨的鎖,和放元素分開,這樣即便有Condition也可使的取和放元素在不一樣的節點上自選
  • notEmpty 取元素的Condition鎖,和放元素鎖分開。
  • putLock notFull 和上面介紹的takeLock notEmpty一直。

經過這種設置,能夠將在鏈表頭上放元素和在鏈表尾部取元素再也不競爭鎖,在必定程度上能夠加快數據處理。指針

相關文章
相關標籤/搜索