java併發程式設計工具類JUC第一篇:BlockingQueue阻塞佇列

字母哥部落格發表於2021-05-31

Java BlockingQueue介面java.util.concurrent.BlockingQueue表示一個可以存取元素,並且執行緒安全的佇列。換句話說,當多執行緒同時從 JavaBlockingQueue中插入元素、獲取元素的時候,不會導致任何併發問題(元素被插入多次、處理多次等問題)。

從java BlockingQueue可以引申出一個概念:阻塞佇列,是指佇列本身可以阻塞執行緒向佇列裡面插入元素,或者阻塞執行緒從佇列裡面獲取元素。比如:當一個執行緒嘗試去從一個空佇列裡面獲取元素的時候,這個執行緒將被阻塞直到佇列內元素數量不再為空。當然,執行緒是否會被阻塞取決於你呼叫什麼方法從BlockingQueue獲取元素,有的方法會阻塞執行緒,有的方法會丟擲異常等等,下文我們會詳細介紹。

一、BlockingQueue 介面實現類

本文不會去介紹如何自己實現BlockingQueue介面,JUC已經為我們做好了相關的一些介面實現類。
BlockingQueue是一個java介面,當我們需要使用阻塞佇列的時候,可以使用它的實現類。java.util.concurrent包裡面有如下的一些實現類實現了BlockingQueue介面。

  • ArrayBlockingQueue
  • DelayQueue
  • LinkedBlockingQueue
  • LinkedBlockingDeque
  • LinkedTransferQueue
  • PriorityBlockingQueue
  • SynchronousQueue

在本文以及後續的文章中,會依次為大家介紹這些實現類的作用及使用場景,期待您的關注。

二、BlockingQueue 應用場景介紹

BlockingQueue通常被應用在一個執行緒生產物件放入佇列,與此同時另一個執行緒消費佇列內的物件的場景下。下面的這張圖說明了使用場景:

生產者執行緒不斷的生產新的物件,並將他們插入到BlockingQueue,直到佇列中object的數量達到佇列儲存容量的上限。也就是說當佇列中物件達到容量上限的時候,生產者執行緒將被阻塞,不能再向佇列中插入新的物件。生產者執行緒將保持阻塞等待狀態,直到消費者執行緒從佇列中拿走Object,讓佇列有空餘位置放入新的物件。

消費者執行緒不斷的從BlockingQueue取出物件並將其進行處理。如果消費者執行緒嘗試從一個空佇列中獲取一個物件,消費者執行緒將被阻塞處於等待狀態,直到生產者向佇列中放入一個新的物件。

所以BlockingQueue經常被用於生產消費的緩衝佇列,如果你不想用分散式的或者中介軟體訊息佇列(redis、kafka)等(因為對於一個小功能會增加比較大的獨立中介軟體運維成本),BlockingQueue可以能是一個備選的選項。

2.1.BlockingQueue 方法介紹

JavaBlockingQueue 提供了四組不同的方法用於向佇列中插入、移除、檢查佇列中包含某一元素物件。每一組方法在被呼叫之後的響應行為上有所不同,如下:

丟擲異常 返回特定值 阻塞後一直等待 阻塞後等待超時
插入物件 add(o) offer(o) put(o) offer(o, timeout, timeunit)
移除物件 remove(o) poll() take() poll(timeout, timeunit)
檢查物件存在 element() peek()

上面的方法的四種行為分別的含義是

  1. 丟擲異常: 如果呼叫方法後不能立即響應結果(空佇列或滿佇列),則丟擲異常。
  2. 返回特定值: 如果呼叫方法後不能立即響應結果(空佇列或滿佇列),則返回特定的值(通常是true/false),true表示方法執行成功,否則表示方法執行失敗。
  3. 阻塞後一直等待: 如果呼叫方法後不能立即響應結果(空佇列或滿佇列),該方法將被阻塞一直處於等待狀態。
  4. 阻塞後等待超時: 如果呼叫方法後不能立即響應結果(空佇列或滿佇列),該方法將在一定時間範圍內被阻塞等待,也就是在超時時間範圍內阻塞。當超出超時時間之後,方法執行緒將不再阻塞,而是返回一個特定的值(通常是true/false),true表示方法執行成功,否則表示方法執行失敗。

另外,BlockingQueue佇列不允許向其內部插入null,如果你向佇列中插入null,將會引發NullPointerException異常。
一般的佇列都是從隊首放入物件,從隊尾獲取物件,BlockingQueue不僅支援從隊首隊尾運算元據物件,還支援從佇列中其他任何位置運算元據。比如:你已經向佇列中放入一個物件並等待處理,但是出於某些特殊原因希望將這個物件從佇列中刪除掉。你可以呼叫remove(o)方法來刪除佇列中的一個特定的o物件。當然我們的程式不能經常性的這樣做,因為佇列這種資料結構經常從中間位置運算元據的效率是極低的,所以除非必要不建議這樣做。

add(o)

BlockingQueueadd() 方法可以將o物件以引數的形式插入到佇列裡面,如果佇列裡面有剩餘空間,將被立即插入;如果佇列裡面沒有剩餘空間,add()方法將跑出 IllegalStateException.

offer(o)

BlockingQueueoffer() 方法可以將o物件以引數的形式插入到佇列裡面,如果佇列裡面有剩餘空間,將被立即插入;如果佇列裡面沒有剩餘空間,offer()方法將返回特定的值false.

offer(o, long millis, TimeUnit timeUnit)

BlockingQueueoffer() 方法有另外一個版本的實現,存在超時時間的設定引數。這個版本的offer()方法將o物件以引數的形式插入到佇列裡面,如果佇列裡面有剩餘空間,將被立即插入;如果佇列裡面沒有剩餘空間,呼叫offer方法的執行緒在超時時間內將被阻塞處於等到狀態,當阻塞時間大於超時時間之後,佇列內如果仍然沒有剩餘空間放入新物件,offer()方法將返回false.

put(o)

BlockingQueueput() 方法可以將o物件以引數的形式插入到佇列裡面,如果佇列裡面有剩餘空間,將被立即插入;如果佇列裡面沒有剩餘空間,呼叫put的方法的執行緒將被阻塞,直到BlockingQueue裡面騰出新的空間可以放入物件為止。

take()

BlockingQueuetake() 方法取出並移除佇列中的第一個元素(物件),如果BlockingQueue佇列中不包含任何的元素,呼叫take()方法的執行緒將被阻塞,直到有新的元素物件插入到佇列中為止。

poll()

BlockingQueuepoll() 方法取出並移除佇列中的第一個元素(物件),如果BlockingQueue佇列中不包含任何的元素,poll()方法將返回null.

poll(long timeMillis, TimeUnit timeUnit)

BlockingQueuepoll(long timeMillis, TimeUnit timeUnit)方法同樣存在一個超時時間限制的版本,正常情況下該方法取出並移除佇列中的第一個元素(物件)。如果BlockingQueue佇列中不包含任何的元素,在超時時間範圍內,如果仍然沒有新的物件放入佇列,這個版本的poll()方法將被阻塞處於等待狀態;當阻塞時間大於超時時間之後,poll(long timeMillis, TimeUnit timeUnit)返回null

remove(Object o)

BlockingQueueremove(Object o) 方法可以從佇列中刪除一個以引數形式給定的元素物件,remove()方法使用o.equals(element)將傳入引數o與佇列中的物件進行一一比對,從而判定要刪除的物件是否在佇列中存在,如果存在就從佇列中刪除並返回true,否則返回false。

需要注意的是:如果佇列中有多個與傳入引數equals相等的物件,只刪除其中一個,不會將佇列中所有匹配的物件都刪除。

peek()

BlockingQueuepeek() 方法將取出佇列中的第一個元素物件,但是並不會將其從佇列中刪除。如果佇列中目前沒有任何的元素,也就是空佇列,peek()方法將返回null.

element()

BlockingQueueelement()方法將取出佇列中的第一個元素物件,但是並不會將其從佇列中刪除。如果佇列中目前沒有任何的元素,也就是空佇列,element()方法將丟擲 NoSuchElementException.

contains(Object o)

BlockingQueuecontains(Object o) 方法用來判斷當前佇列中是否存在某個物件,該物件與傳入引數o相等(Objects.equals(o, element)被用來判定物件的相等性)。遍歷佇列中的所有元素,一旦在佇列中發現匹配的元素物件,該方法將返回true;如果沒有任何的元素匹配相等,該方法返回false。

drainTo(Collection dest)

drainTo(Collection dest)方法一次性的將佇列中的所有元素取出到集合類Collection dest物件中儲存。

drainTo(Collection dest, int maxElements)

drainTo(Collection dest)方法一次性的從佇列中取出maxElements個元素到集合類Collection dest物件中儲存。

size()

BlockingQueuesize() 方法返回佇列中目前共有多少個元素

remainingCapacity()

BlockingQueueremainingCapacity() 方法將返回佇列目前還剩多少個可用空間用於放入新的物件。剩餘空間容量=佇列的總容量-已經被佔用的空間數量

歡迎關注我的部落格,裡面有很多精品合集

  • 本文轉載註明出處(必須帶連線,不能只轉文字):字母哥部落格

覺得對您有幫助的話,幫我點贊、分享!您的支援是我不竭的創作動力! 。另外,筆者最近一段時間輸出瞭如下的精品內容,期待您的關注。

相關文章