1. 程式人生 > >阻塞佇列BlockingQueue以及它的兩個重要實現類ArrayBlockingQueue和LinkedBlockingQueue

阻塞佇列BlockingQueue以及它的兩個重要實現類ArrayBlockingQueue和LinkedBlockingQueue

多執行緒環境中,通過佇列可以很容易實現資料共享,比如經典的“生產者”和“消費者”模型中,通過佇列可以很便利地實現兩者之間的資料共享。假設我們有若干生產者執行緒,另外又有若干個消費者執行緒。如果生產者執行緒需要把準備好的資料共享給消費者執行緒,利用佇列的方式來傳遞資料,就可以很方便地解決他們之間的資料共享問題。但如果生產者和消費者在某個時間段內,萬一發生資料處理速度不匹配的情況呢?理想情況下,如果生產者產出資料的速度大於消費者消費的速度,並且當生產出來的資料累積到一定程度的時候,那麼生產者必須暫停等待一下(阻塞生產者執行緒),以便等待消費者執行緒把累積的資料處理完畢,反之亦然。

阻塞隊BlockingQueue繼承自Queue。

除了實現了Queue的方法之外,還擴充套件了兩個阻塞方法。

put(anObject):把anObject加到BlockingQueue裡,如果BlockQueue沒有空間,則呼叫此方法的執行緒被阻斷直到BlockingQueue裡面有空間再繼續.

take():取走BlockingQueue裡排在首位的物件,若BlockingQueue為空,阻斷進入等待狀態直到BlockingQueue有新的資料被加入; 

ArrayBlockingQueue和LinkedBlockingQueue是兩個最普通也是最常用的阻塞佇列,一般情況下,在處理多執行緒間的生產者消費者問題,使用這兩個類足以。

1. ArrayBlockingQueue
  基於陣列的阻塞佇列實現,在ArrayBlockingQueue內部,維護了一個定長陣列,以便快取佇列中的資料物件,這是一個常用的阻塞佇列,除了一個定長陣列外,ArrayBlockingQueue內部還儲存著兩個整形變數putindex,takeindex,分別標識著佇列的頭部和尾部在陣列中的位置。其結構圖過於簡單,不再畫出來。

現在直接來看插入佇列的實現,如下圖所示:

實現很簡單,直接在陣列的putindex的位置放入這個新元素就可以了,

放入之後觸發獲取等待中的獲取執行緒。

 

注意了,中間還有一句,如果放入新元素後putindex等於陣列長度了,就把putindex置為0,什麼意思呢?就是又從陣列的第一個位置開始放入資料。有人可能就又疑問了,又從第一個元素開始放新插入的元素,那不是會覆蓋之前插入的第一個元素嗎?不會的,因為呼叫入隊操作的offer,put等方法都做了判斷,如果陣列滿了,是不會插入新元素進去的。而是等元素被取走之後,陣列沒滿才會在這個位置插入新元素。非常簡單巧妙卻非常實用的設計。再結合出隊方法,就可以知道這個的實現是何等的輕量級。出隊方法如下圖:

出隊也很簡單,直接取出陣列takeindex位置的數就行了,取出之後,再把這個位置置空。如果下一次take的位置等於陣列的長度了,那麼就又從0開始取。取完之後,通知等待中的生產執行緒可以生產了。

利用這種方式,入隊和出隊操作,如此簡單便捷輕量級,不需要像對普通陣列作增刪一樣,要移動整個陣列。

正是因為如此簡單,所以入會和出隊方法offer,poll這些,使用了同一個鎖對像,效率也很高。而不像LinkedBlockingQueue,讀和取使用兩個不同的鎖物件。

 

2. LinkedBlockingQueue
  基於連結串列的阻塞佇列,同ArrayListBlockingQueue類似,其內部也維持著一個數據緩衝佇列(該佇列由一個連結串列構成),當生產者往佇列中放入一個數據時,佇列會從生產者手中獲取資料,並快取在佇列內部,而生產者立即返回;只有當佇列緩衝區達到最大值快取容量時(LinkedBlockingQueue可以通過建構函式指定該值),才會阻塞生產者佇列,直到消費者從佇列中消費掉一份資料,生產者執行緒會被喚醒,反之對於消費者這端的處理也基於同樣的原理。

LinkedBlockingQueue的阻塞方法的插入和取出的實現其實非常簡單。插入的時候,就是看當前容量是不是已經最大了,如果是就通過Condition類的await中斷執行緒,讓出鎖等待其他執行緒執行取出方法後,呼叫Condition類的signal()重新觸發執行。

取出的時候,也很簡單,如果沒有就釋放鎖進入等待,等待其他執行緒執行插入方法後被喚醒。

最大的不同,ABQ雖然用一個鎖物件,但是增刪快。LBQ雖然用兩個鎖物件,但是增刪之前還要封裝成Node物件,也有時間消耗,對GC也有影響。所以真不好說什麼情況下用哪一個實現類了。只有在實際情況測試了。