
Java BlockingQueue接口`java.util.concurrent.BlockingQueue`表示一個可以存取元素,并且線程安全的隊列。換句話說,當多線程同時從 Java`BlockingQueue`中插入元素、獲取元素的時候,不會導致任何并發問題(元素被插入多次、處理多次等問題)。
從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 方法介紹
Java`BlockingQueue` 提供了四組不同的方法用于向隊列中插入、移除、檢查隊列中包含某一元素對象。每一組方法在被調用之后的響應行為上有所不同,如下:
||拋出異常|返回特定值|阻塞后一直等待|阻塞后等待超時|
|---|---|---|---|---|
|插入對象|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)
BlockingQueue`add()` 方法可以將o對象以參數的形式插入到隊列里面,如果隊列里面有剩余空間,將被立即插入;如果隊列里面沒有剩余空間,`add()`方法將跑出 IllegalStateException.
#### offer(o)
BlockingQueue`offer()` 方法可以將o對象以參數的形式插入到隊列里面,如果隊列里面有剩余空間,將被立即插入;如果隊列里面沒有剩余空間,`offer()`方法將返回特定的值`false`.
#### offer(o, long millis, TimeUnit timeUnit)
BlockingQueue`offer()` 方法有另外一個版本的實現,存在超時時間的設置參數。這個版本的`offer()`方法將o對象以參數的形式插入到隊列里面,如果隊列里面有剩余空間,將被立即插入;如果隊列里面沒有剩余空間,調用offer方法的線程在超時時間內將被阻塞處于等到狀態,當阻塞時間大于超時時間之后,隊列內如果仍然沒有剩余空間放入新對象,`offer()`方法將返回`false`.
#### put(o)
BlockingQueue`put()` 方法可以將o對象以參數的形式插入到隊列里面,如果隊列里面有剩余空間,將被立即插入;如果隊列里面沒有剩余空間,調用put的方法的線程將被阻塞,直到BlockingQueue里面騰出新的空間可以放入對象為止。
#### take()
BlockingQueue`take()` 方法取出并移除隊列中的第一個元素(對象),如果BlockingQueue隊列中不包含任何的元素,調用`take()`方法的線程將被阻塞,直到有新的元素對象插入到隊列中為止。
#### poll()
BlockingQueue`poll()` 方法取出并移除隊列中的第一個元素(對象),如果BlockingQueue隊列中不包含任何的元素,`poll()`方法將返回`null`.
#### poll(long timeMillis, TimeUnit timeUnit)
BlockingQueue`poll(long timeMillis, TimeUnit timeUnit)`方法同樣存在一個超時時間限制的版本,正常情況下該方法取出并移除隊列中的第一個元素(對象)。如果BlockingQueue隊列中不包含任何的元素,在超時時間范圍內,如果仍然沒有新的對象放入隊列,這個版本的`poll()`方法將被阻塞處于等待狀態;當阻塞時間大于超時時間之后,`poll(long timeMillis, TimeUnit timeUnit)`返回`null`
#### remove(Object o)
BlockingQueue`remove(Object o)` 方法可以從隊列中刪除一個以參數形式給定的元素對象,`remove()`方法使用`o.equals(element)`將傳入參數o與隊列中的對象進行一一比對,從而判定要刪除的對象是否在隊列中存在,如果存在就從隊列中刪除并返回true,否則返回false。
需要注意的是:如果隊列中有多個與傳入參數equals相等的對象,只刪除其中一個,不會將隊列中所有匹配的對象都刪除。
#### peek()
BlockingQueue`peek()` 方法將取出隊列中的第一個元素對象,但是并不會將其從隊列中刪除。如果隊列中目前沒有任何的元素,也就是空隊列,`peek()`方法將返回`null`.
#### element()
BlockingQueue`element()`方法將取出隊列中的第一個元素對象,但是并不會將其從隊列中刪除。如果隊列中目前沒有任何的元素,也就是空隊列,`element()`方法將拋出 NoSuchElementException.
#### contains(Object o)
BlockingQueue`contains(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()
BlockingQueue`size()` 方法返回隊列中目前共有多少個元素
#### remainingCapacity()
BlockingQueue`remainingCapacity()` 方法將返回隊列目前還剩多少個可用空間用于放入新的對象。剩余空間容量=隊列的總容量-已經被占用的空間數量
- 線程
- 1.進程和線程-鎖與信號量
- 2.Thread類線程狀態轉換
- 2.并發與并行-同步與異步
- 4.線程池
- 5.對象級別與類級別的同步鎖
- 6.創建線程的四種方式
- 7.臨界區-阻塞-活鎖-死鎖
- 2.JMM多線程模型
- JUC
- BlockingQueue
- ArrayBlockingQueue
- DelayQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- SynchronousQueue
- BlockingDeque
- ConcurrentHashMap
- CountDownLatch
- CyclicBarrier
- Exchanger
- AtomicInteger
- Lock
- Condition
- ReentrantLock讀寫鎖
- StampedLock
- Semaphore