java並發包提供的三種常用並發隊列實現


java並發包中提供了三個常用的並發隊列實現,分別是:ConcurrentLinkedQueue、LinkedBlockingQueue和ArrayBlockingQueue。

ConcurrentLinkedQueue使用的是CAS原語無鎖隊列實現,是一個異步隊列,入隊速度很快,出隊進行了加鎖,性能稍慢;

LinkedBlockingQueue也是阻塞隊列,入隊和出隊都用了加鎖,當隊空的時候線程會暫時阻塞;當隊空的時候線程會暫時阻塞

ArrayBlockingQueue是初始容器固定的阻塞隊列,我們可以用來作為數據庫模塊成功競拍的隊列,比如有10個商品,那么我們就設定一個10大小的數組隊列。

一、BlockingQueue接口

      BlockingQueue接口定義了一種阻塞的FIFO queue,每一個BlockingQueue都有一個容量,讓容量滿時往BlockingQueue中添加數據時會造成阻塞,當容量為空時取元素操作會阻塞。

二、ArrayBlockingQueue

    ArrayBlockingQueue是一個由數組支持的有界阻塞隊列。在讀寫操作上都需要鎖住整個容器,因此吞吐量與一般的實現是相似的,適合於實現“生產者消費者”模式。

三、LinkedBlockingQueue

     LinkedBlockingQueue內部維持着一個數據緩沖隊列(該隊列由一個鏈表構成),當生產者往隊列中放入一個數據時,隊列會從生產者手中獲取數據,並緩存在隊列內部,而生產者立即返回;只有當隊列緩沖區達到最大值緩存容量時(LinkedBlockingQueue可以通過構造函數指定該值),才會阻塞生產者隊列,直到消費者從隊列中消費掉一份數據,生產者線程會被喚醒,反之對於消費者這端的處理也基於同樣的原理。而LinkedBlockingQueue之所以能夠高效的處理並發數據,還因為其對於生產者端和消費者端分別采用了獨立的鎖來控制數據同步,這也意味着在高並發的情況下生產者和消費者可以並行地操作隊列中的數據,以此來提高整個隊列的並發性能。

  3. 隊列大小初始化方式不同
     ArrayBlockingQueue是有界的,必須指定隊列的大小;
     LinkedBlockingQueue是無界的,可以不指定隊列的大小,但是默認是Integer.MAX_VALUE。當然也可以指定隊列大小,從而成為有界的;

注意:
1.    在使用LinkedBlockingQueue時,若用默認大小且當生產速度大於消費速度時候,有可能會內存溢出;
2.    在使用ArrayBlockingQueue和LinkedBlockingQueue分別對1000000個簡單字符做入隊操作時,
       LinkedBlockingQueue的消耗是ArrayBlockingQueue消耗的10倍左右,
       即LinkedBlockingQueue消耗在1500ms左右,而ArrayBlockingQueue只需150ms左右。

性能測試:不限容量的LinkedBlockingQueue的吞吐量 > ArrayBlockingQueue > 限定容量的LinkedBlockingQueue

     LinkedBlockingQueue內部使用ReentrantLock實現插入鎖(putLock)和取出鎖(takeLock)。putLock上的條件變量是notFull,即可以用notFull喚醒阻塞在putLock上的線程。takeLock上的條件變量是notEmtpy,即可用notEmpty喚醒阻塞在takeLock上的線程。

四、ArrayBlockingQueue和LinkedBlockingQueue的區別

     1. 隊列中鎖的實現不同

   ArrayBlockingQueue實現的隊列中的鎖是沒有分離的,即生產和消費用的是同一個鎖;

     LinkedBlockingQueue實現的隊列中的鎖是分離的,即生產用的是putLock,消費是takeLock;

2. 在生產或消費時操作不同

     ArrayBlockingQueue基於數組,在生產和消費的時候,是直接將枚舉對象插入或移除的,不會產生或銷毀任何額外的對象實例;
     LinkedBlockingQueue基於鏈表,在生產和消費的時候,需要把枚舉對象轉換為Node<E>進行插入或移除,會生成一個額外的Node對象,這在長時間內需要高效並發地處理大批量數據的系統中,其對於GC的影響還是存在一定的區別;

注意:
1.    在使用LinkedBlockingQueue時,若用默認大小且當生產速度大於消費速度時候,有可能會內存溢出;
2.    在使用ArrayBlockingQueue和LinkedBlockingQueue分別對1000000個簡單字符做入隊操作時,
       LinkedBlockingQueue的消耗是ArrayBlockingQueue消耗的10倍左右,
       即LinkedBlockingQueue消耗在1500ms左右,而ArrayBlockingQueue只需150ms左右。

性能測試:不限容量的LinkedBlockingQueue的吞吐量 > ArrayBlockingQueue > 限定容量的LinkedBlockingQueue

五、添加、刪除元素的區別

添加元素的方法有三個:add,put,offer

1、add方法: LinkedBlockingQueue構造的時候若沒有指定大小,則默認大小為Integer.MAX_VALUE,當然也可以在構造函數的參數中指定大小。LinkedBlockingQueue不接受null。add方法在添加元素的時候,若超出了度列的長度會直接拋出異常;

2、put方法:若向隊尾添加元素的時候發現隊列已經滿了會發生阻塞一直等待空間,以加入元素;

3、offer方法:  offer方法在添加元素時,如果發現隊列已滿無法添加的話,會直接返回false。

poll: 若隊列為空,返回null。

remove:若隊列為空,拋出NoSuchElementException異常。

take:若隊列為空,發生阻塞,等待有元素。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM