Java中的阻塞隊列簡介

生產者消費者模型相信大家都知道,我們使用普通的任務隊列時要手動防止併發,代碼比較繁瑣,JDK給我們提供了線程安全的阻塞隊列BlockingQueue來簡化我們的代碼。

總覽

BlockingQueue是一個接口,他定義了阻塞隊列中的總體行爲,我們來看一下他的主要方法

他給我們提供了多種取出與存放操作,根據上面的圖片就可以清楚的瞭解各種方法不同行爲。阻塞隊列並不允許存放null元素,阻塞隊列分爲有界和無界隊列,有界即需要指定隊列容量,無界則隊列容量上限爲Integer.MAX_VALUE。

JDK一共提供了7種BlockingQueue的實現類(其實在ScheduledThreadPoolExecutor中還有一個內部類,這裏不討論),我們分別簡單介紹下。

1.ArrayBlockingQueue

由數組實現的有界阻塞隊列,使用時必須指定容量大小。這裏寫順便分析下他如何支持多線程的。先來看下他的幾個實例域

主要通過上面的倆個條件Condition來實現生產者與消費者的協調,condition用來與lock共同實現線程的等待通知機制(也可以用原來的synchronized與object的wait和notify來實現)。接下來看入隊的方法

這裏以阻塞的put方法爲例,首先獲取鎖,判斷數組中元素是否已經滿了,滿了的話調用notFull的await方法,直到有消費者消費掉元素後調用notFull的signal方法後隊列有剩餘空間才能繼續入隊。數組有空閒容量的情況下調用enqueue方法,將元素放入數組後,調用notEmpty的signal方法,告訴消費者隊列中已經有元素了,可以來消費了。

再來看與之對應的阻塞的take方法

邏輯基本就是和put方法相反的,數組爲空時,await等待,直到隊列有元素時notEmpty.notify通知進行消費,消費之後通知生產者隊列有空餘位置。

2.DelayQueue

無界延時隊列,他可以在指定時間後取出任務。

可以看到,該隊列中的元素必須繼承Delayed接口,而Delayed又繼承了Comparable接口,所以元素就必須實現Delayed中的getDelay方法用於獲取延時時間,與Comparable中的compareTo方法用於比較時間。

put的時候其實是調用了PriorityQueue的offer方法,PriorityQueue爲優先級隊列,底層由堆實現(對堆不太瞭解的小夥伴建議閱讀紅色算法書第四版,比那本經典的算法要簡單一些而且書中的數據結構都是使用java實現的),DelayQueue將最先執行的任務放在隊頭。

取出方法如圖,首先取出隊頭元素判斷delay時間是否小於等於0,若是則代表立即執行,若不是調用condition的awaitNanos等待相應時間後再執行。這裏getDelay方法的實現是比較重要的

基本上是按照這樣的模式來實現自己的延時任務類。

3.LinkedBlockingDeque

基於鏈表實現的可選擇界限的雙端阻塞隊列,可選擇界限意味着可以在構造函數中指定容量,也可以不指定,不指定意味着容量爲Integer.MAX_VALUE.

4.LinkedBlockingQueue

基於鏈表實現的可選擇界限的阻塞隊列

5.LinkedTransferQueue

引用自http://ifeve.com/java-blocking-queue/:基於鏈表實現的無界Transfer隊列。TransferQueue是一個生產者有可能等待消費者去接收的阻塞隊列,它主要有倆個方法,transfer:如果當前有消費者正在等待接收元素(消費者使用take()方法或帶時間限制的poll()方法時),transfer方法可以把生產者傳入的元素立刻transfer給消費者。如果沒有消費者在等待接收元素,transfer方法會將元素存放在隊列的tail節點,並等到該元素被消費者消費了才返回;tryTransfer:用來試探生產者傳入的元素是否能直接傳給消費者。如果沒有消費者等待接收元素,則返回false。和transfer方法的區別是tryTransfer方法無論消費者是否接收,方法立即返回。而transfer方法是必須等到消費者消費了才返回。對於帶有時間限制的tryTransfer(E e, long timeout, TimeUnit unit)方法,則是試圖把生產者傳入的元素直接傳給消費者,但是如果沒有消費者消費該元素則等待指定的時間再返回,如果超時還沒消費元素,則返回false,如果在超時時間內消費了元素,則返回true。

6.PriorityBlockingQueue

基於堆實現的優先級阻塞隊列,默認按元素的自然順序排序。

7.SynchronousQueue

每個插入操作必須等待另一個線程的相應移除操作的隊列,該隊列是不能存儲元素的,可以看成是一個傳送帶。

最後:我寫着寫着就總感覺和以前看過的一篇文章好像雷同了(尷尬),找了好久終於找到那篇博文了http://ifeve.com/java-blocking-queue/,果然是受了這篇文章的影響,這篇文章作者是螞蟻金服技術專家方騰飛。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章