歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java Queue 使用總結

Java Queue 使用總結

日期:2017/3/1 9:08:51   编辑:Linux編程

Queue概覽

如圖所示,在並發隊列上,JDK提供了2套實現,一個是以ConcurrentLinkedQueue為代表的高性能非阻塞隊列,一個是以BlockingQueue接口為代表的阻塞隊列,無論哪種都繼承自Queue。使用阻塞算法的隊列可以用一個鎖(入隊和出隊用同一把鎖)或兩個鎖(入隊和出隊用不同的鎖)等方式來實現,而非阻塞的實現方式則可以使用循環CAS的方式來實現,下面我們來一一分析。

ConcurrentLinkedQueue
一個適用於高並發場景下的隊列,通過無鎖的方式(CAS+volatile),實現了高並發下的高性能,通常ConcurrentLinkedQueue的性能好於BlockingQueue。
它是一個基於鏈接節點的無界線程安全隊列,遵循先進先出的原則,頭是最先加入的,尾是最近加入的,不允許加入null元素。
注意add()/offer()都是加入元素的方法,這裡沒有區別;poll()/peek()是取出頭元素的方法,區別點在於poll會刪除元素,而peek不會。
要特別注意到由於它的非阻塞性,並不像其他普通集合那樣,獲取隊列的SIZE的方法並不是常量時間的花費,而是O(N)的,因此我們應該盡可能避免使用size()方法,可以考慮使用isEmpty()代替。
雖然使用到了CAS+VOLATILE的機制避免了鎖,但是我們要明白的是,這只是保證單個操作,如peek()的安全,但是多個操作如果想保證的話,需要使用鎖機制來達到同步的效果。

BlockingQueue API
入隊:

offer(E e):如果隊列沒滿,立即返回true; 如果隊列滿了,立即返回false-->不阻塞
put(E e):如果隊列滿了,一直阻塞,直到數組不滿了或者線程被中斷-->阻塞
offer(E e, long timeout, TimeUnit unit):在隊尾插入一個元素,,如果數組已滿,則進入等待,直到等待時間超時


出隊:
poll():非阻塞拿數據,立即返回

take():阻塞拿數據
poll(long timeout, TimeUnit unit):帶有一定超時時間的poll拿取數據

ArrayBlockingQueue

基於數組的阻塞隊列實現,在其內部維護了一個定長數組,以便緩存隊列中的數據對象,由於ArrayBlockingQueue內部只有一個鎖對象(ReentrantLock),因此讀寫沒有實現分離,也就意味著生產消費不能完全並行。由於長度需要定義,因此也叫有界隊列。

LinkedBlockingQueue
基於鏈表的阻塞隊列實現,同ArrayBlockingQueue類似,其內部也維持著一個數據緩沖隊列(鏈表構成)。
LinkedBlockingQueue之所以較ArrayBlockingQueue更加高效的處理並發數據,是因為內部實現采用了2把鎖,也就是實現了入隊、出隊分別上鎖,即讀寫分離,從而生產者、消費者完全到達了並行。
無需定義長度,也叫無界隊列。當然不定義長度時,需要注意下生產者的速度和消費者的速度,因為默認情況下隊列長度是Integer.MAX_VALUE。

SynchronousQueue

一個沒有緩沖的隊列,生產者生產的數據會直接被消費者獲取到並消費。它是一個輕量級的阻塞隊列,因為不具備容量,在用法上,只能是一個線程阻塞著取元素,等待另一個線程往隊列裡面放入一個元素,然後會被等待著的線程立即取走,其實就是實現了線程間的輕量級的單元素交換。

PriorityBlockingQueue
基於優先級的阻塞隊列(優先級的判斷通過構造函數傳入的Compator對象決定,也就是傳入隊列中對象必須實現Comparable接口)。
在實現PriorityBlockingQueue時,內部控制線程同步的鎖采用的是公平鎖,也是一個無界的隊列。
通俗的來說,不是先進先出的隊列了,而是誰的優先級低誰先出去。那麼可以思考下,是否每次add/offer都會進行一次排序呢?我們是否需要按照優先級進行全排序呢?實際上,可以大致看一看add/take方法,會了解到PriorityBlockingQueue的設計思想:在add時,並不進行排序處理,當進行take時,選擇優先級最小的拿出來而已,這樣既避免了在add時花費時間排序,又在take時節省了時間,因為並沒有全排序,僅僅是挑選了一個優先級低的元素而已。

DelayQueue

帶有延遲時間的Queue,其中的元素只有當指定的延遲時間到了,才能從隊列中獲取到該元素。隊列中的元素必須實現Delayed接口,沒有大小限制。本質上來說,是借助於PriorityBlockingQueue來實現的,以延遲時間作為優先級。延遲隊列的應用場景很多,比如緩存超時的數據進行移除,任務超時處理,空閒連接的關閉等等。

Copyright © Linux教程網 All Rights Reserved