ArrayBlockingQueue 與 LinkedBlockingQueue

http://www.javashuo.com/article/p-nidzvcyi-p.htmljava

  • ArrayBlockingQueue內部的阻塞隊列是經過重入鎖ReenterLockCondition條件隊列實現的,
    • 因此ArrayBlockingQueue中的元素存在公平訪問與非公平訪問的區別,
    • 對於公平訪問隊列,被阻塞的線程能夠按照阻塞的前後順序訪問隊列,
      • 即先阻塞的線程先訪問隊列。
    • 而非公平隊列,當隊列可用時,阻塞的線程將進入爭奪訪問資源的競爭中,
      • 也就是說誰先搶到誰就執行,沒有固定的前後順序。
    • //默認非公平阻塞隊列
      ArrayBlockingQueue queue = new ArrayBlockingQueue(2);
      //公平阻塞隊列
      ArrayBlockingQueue queue1 = new ArrayBlockingQueue(2,true);
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    /** 存儲數據的數組 */
    final Object[] items;

    /**獲取數據的索引,主要用於take,poll,peek,remove方法 */
    int takeIndex;

    /**添加數據的索引,主要用於 put, offer, or add 方法*/
    int putIndex;

    /** 隊列元素的個數 */
    int count;


    /** 控制並不是訪問的鎖 */
    final ReentrantLock lock;

    /**notEmpty條件對象,用於通知take方法隊列已有元素,可執行獲取操做 */
    private final Condition notEmpty;

    /**notFull條件對象,用於通知put方法隊列未滿,可執行添加操做 */
    private final Condition notFull;

    /**
       迭代器
     */
    transient Itrs itrs = null;

}
  • ArrayBlockingQueue內部確實是經過數組對象items來存儲全部的數據
    • 一個ReentrantLock來同時控制添加線程移除線程的併發訪問,
      • 這點與LinkedBlockingQueue區別很大(稍後會分析)
    • notEmpty條件對象則是用於存放等待喚醒調用take方法的線程,
      • 告訴他們隊列已有元素,能夠執行獲取操做
    • notFull條件對象是用於等待或喚醒調用put方法的線程,
      • 告訴它們,隊列未滿,能夠執行添加元素的操做
    • 只要putIndex與takeIndex不相等就說明隊列沒有結束

LinkedBlockingQueuenode

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    /**
     * 節點類,用於存儲數據
     */
    static class Node<E> {
        E item;

        /**
         * One of:
         * - the real successor Node
         * - this Node, meaning the successor is head.next
         * - null, meaning there is no successor (this is the last node)
         */
        Node<E> next;

        Node(E x) { item = x; }
    }

    /** 阻塞隊列的大小,默認爲Integer.MAX_VALUE */
    private final int capacity;

    /** 當前阻塞隊列中的元素個數 */
    private final AtomicInteger count = new AtomicInteger();

    /**
     * 阻塞隊列的頭結點
     */
    transient Node<E> head;

    /**
     * 阻塞隊列的尾節點
     */
    private transient Node<E> last;

    /** 獲取並移除元素時使用的鎖,如take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** notEmpty條件對象,當隊列沒有數據時用於掛起執行刪除的線程 */
    private final Condition notEmpty = takeLock.newCondition();

    /** 添加元素時使用的鎖如 put, offer, etc */
    private final ReentrantLock putLock = new ReentrantLock();

    /** notFull條件對象,當隊列數據已滿時用於掛起執行添加的線程 */
    private final Condition notFull = putLock.newCondition();

}
  • 由鏈表實現的有界隊列阻塞隊列,但大小默認值爲Integer.MAX_VALUE
    • 因此咱們在使用LinkedBlockingQueue時建議手動傳值,
    • 爲其提供咱們所需的大小,
    • 避免隊列過大形成機器負載或者內存爆滿等狀況。
      • 若是存在添加速度大於刪除速度時候,
      • 有可能會內存溢出,這點在使用前但願慎重考慮
  • 在正常狀況下,連接隊列的吞吐量要高於基於數組的隊列(ArrayBlockingQueue)
    • 由於其內部實現添加刪除操做使用的兩個ReenterLock來控制併發執行,
  • 內部維持一個基於鏈表的數據隊列
    • 每一個添加到LinkedBlockingQueue隊列中的數據都將被封裝成Node節點,
    • 添加的鏈表隊列中,其中head和last分別指向隊列的頭結點和尾結點
    • 內部分別使用了takeLock 和 putLock 對併發進行控制,
      • 也就是說,添加和刪除操做並非互斥操做,
      • 能夠同時進行,這樣也就能夠大大提升吞吐量
  • remove方法刪除指定的對象,爲何同時對putLock和takeLock加鎖?
    • remove(Object o)
      • 刪除指定元素
    • 這是由於remove方法刪除的數據的位置不肯定,
    • 爲了不形成並不是安全問題,因此須要對2個鎖同時加鎖。
    • 其餘移除隊尾的操做,一個takeLock鎖夠了
相關文章
相關標籤/搜索