java中的線程安全隊列


在並發編程中,有時候需要使用線程安全的隊列,如果要實現一個線程安全的隊列有兩種實現方式:阻塞算法、非阻塞算法。

使用阻塞算法的隊列可以用一個鎖(出入隊列用同一把鎖),或兩個鎖(入隊和出隊用不同的鎖),非阻塞的實現方式則可以用循環CAS的方式實現。

一 非阻塞方式實現線程安全的隊列

ConcurrentLinkedQueue

 ConcurrentLinkedQueue由head節點和tail節點組成,每個節點node由節點元素item和指向下一個節點next的引用組成。當我們增加一個元素時,它會添加到隊列的末尾,當我們取一個元素時,它會返回一個隊列頭部的元素。

雖然ConcurrentLinkedQueue的性能很好,但是在調用size()方法的時候,會遍歷一遍集合,對性能損害較大,執行很慢,因此應該盡量的減少使用這個方法,如果判斷是否為空,最好用isEmpty()方法

 ConcurrentLinkedQueue不允許插入null元素,會拋出空指針異常。

 ConcurrentLinkedQueue是無界的,所以使用時,一定要注意內存溢出的問題。即對並發不是很大中等的情況下使用,不然占用內存過多或者溢出,對程序的性能影響很大,甚至是致命的。 

二 阻塞方式實現線程安全的對列

JDK7提供了7個阻塞隊列,如下。
口 ArrayBlockingqueue:ー個由數組結構組成的有界阻塞隊列。
口 LinkedBlockingqueue:一個由鏈表結構組成的有界阻塞隊列。
口 PriorityBlockingqueue:一個支持優先級排序的無界阻塞隊列。
口 Delayqueue:ー個使用優先級隊列實現的無界阻塞隊列。
口 Synchronousqueue:一個不存儲元素的阻塞隊列。
口 Linkedtransferqueue:ー個由鏈表結構組成的無界阻塞隊列。
口 LinkedblockingDeque:ー個由鏈表結構組成的雙向阻塞隊列。

 

ArrayBlockingQueue 

 

LinkedBlockingQueue 

 


 ConcurrentLinkedQueue、ArrayBlockingQueue、LinkedBlockingQueue 區別及使用場景

ArrayBlockingQueue extends AbstractQueue implements BlockingQueue 
LinkedBlockingQueue extends AbstractQueue implements BlockingQueue
ConcurrentLinkedQueue extends AbstractQueue implements Queue

ConcurrentLinkedQueue基於CAS的無鎖技術,不需要在每個操作時使用鎖,所以擴展性表現要更加優異,在常見的多線程訪問場景,一般可以提供較高吞吐量。

LinkedBlockingQueue內部則是基於鎖,並提供了BlockingQueue的等待性方法。

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

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

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

 


 LinkedBlockingQueue與ArrayBlockingQueue的異同

相同:

1、LinkedBlockingQueue和ArrayBlockingQueue都實現了BlockingQueue接口;

2、LinkedBlockingQueue和ArrayBlockingQueue都是可阻塞的隊列 

  內部都是使用ReentrantLock和Condition來保證生產和消費的同步;

  當隊列為空,消費者線程被阻塞;當隊列裝滿,生產者線程被阻塞;

使用Condition的方法來同步和通信:await()和signal()

不同:

1、由上圖可以看出,他們的鎖機制不同

  LinkedBlockingQueue中的鎖是分離的,生產者的鎖PutLock,消費者的鎖takeLock

  而ArrayBlockingQueue生產者和消費者使用的是同一把鎖;

2、他們的底層實現機制也不同

  LinkedBlockingQueue內部維護的是一個鏈表結構

在生產和消費的時候,需要創建Node對象進行插入或移除,大批量數據的系統中,其對於GC的壓力會比較大

而ArrayBlockingQueue內部維護了一個數組

在生產和消費的時候,是直接將枚舉對象插入或移除的,不會產生或銷毀任何額外的對象實例

 3、構造時候的區別

  LinkedBlockingQueue有默認的容量大小為:Integer.MAX_VALUE,當然也可以傳入指定的容量大小

ArrayBlockingQueue在初始化的時候,必須傳入一個容量大小的值

  看其提供的構造方法就能知道

4、執行clear()方法

  LinkedBlockingQueue執行clear方法時,會加上兩把鎖

5、統計元素的個數

 LinkedBlockingQueue中使用了一個AtomicInteger對象來統計元素的個數

 

 ArrayBlockingQueue則使用int類型來統計元素


 

阻塞隊列的實現

https://segmentfault.com/a/1190000000373535

 

 

 

參考:

https://www.jb51.net/article/90899.htm

https://blog.csdn.net/jameshadoop/article/details/52729796

 


免責聲明!

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



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