阻塞佇列一——java中的阻塞佇列
阿新 • • 發佈:2020-06-11
##目錄
- 阻塞佇列簡介:介紹阻塞佇列的特性與應用場景
- java中的阻塞佇列:介紹java中實現的供開發者使用的阻塞佇列
- `BlockQueue`中方法:介紹阻塞佇列的`API`介面
- 阻塞佇列的實現原理:具體的例子說明阻塞佇列的實現原理
- 總結
##阻塞佇列簡介
阻塞佇列(BlockingQueue)首先是一個支援先進先出的佇列,與普通的佇列完全相同;
其次是一個支援阻塞操作的佇列,即:
- 當佇列滿時,會阻塞執行插入操作的執行緒,直到佇列不滿。
- 當佇列為空時,會阻塞執行獲取操作的執行緒,直到佇列不為空。
阻塞佇列用在多執行緒的場景下,因此阻塞佇列使用了鎖機制來保證同步,這裡使用的可重入鎖;
而對於阻塞與喚醒機制則有與鎖繫結的`Condition`實現
應用場景:生產者消費者模式
##java中的阻塞佇列
java中的阻塞佇列根據容量可以分為有界佇列和無界佇列:
- 有界佇列:佇列中只能儲存有限個元素,超出後存放元素執行緒會被阻塞或者失敗。
- 無界佇列:佇列中可以儲存無限個元素。
java8中提供了7種阻塞佇列阻塞佇列供開發者使用,如下表:
|類名|描述|
|:---:|:---:|
|ArrayBlockingQueue|一個由陣列結構組成的有界阻塞佇列|
|LinkedBlockingQueue|由連結串列結構組成的有界阻塞佇列(預設大小Integer.MAX_VALUE)|
|PriorityBlockingQueue|支援優先順序排序的無界阻塞佇列|
|DelayQueue|使用優先順序佇列實現的延遲無界阻塞佇列|
|SynchronousQueue|不儲存元素的阻塞佇列,即單個元素的佇列|
|LinkedTransferQueue|由連結串列結構組成的無界阻塞佇列|
|LinkedBlockingDeque|由連結串列結構組成的雙向阻塞佇列|
另外還有一個在`ScheduledThreadPoolExecutor`中實現的`DelayedWorkQueue`阻塞佇列,
但這個阻塞佇列開發者不能使用。它們之間的UML類圖如下圖:
![](https://img2020.cnblogs.com/blog/1669869/202006/1669869-20200611095645730-1604868536.png)
`BlockingQueue`介面是阻塞佇列對外的訪問介面,所有的阻塞佇列都實現了`BlockQueue`中的方法
## `BlockQueue`中方法
作為一個佇列的核心方法就是入隊和出隊。由於存在阻塞策略,`BlockQueue`將出隊入隊的情況分為了四組,每組提供不同的方法:
- 丟擲異常:當佇列滿時,如果再往佇列中插入元素,則丟擲`IllegalStateException`異常;
當佇列為空時,從佇列中獲取元素則丟擲`NoSuchElementException`異常。
- 返回特定值(布林值):當佇列滿時,如果再往佇列中插入元素,則返回false;當佇列為空時,從佇列中獲取元素則返回null。
- 一直阻塞:當佇列滿時,如果再往佇列中插入元素,阻塞當前執行緒直到佇列中至少一個被移除或者響應中斷退出;
當佇列為空時,則阻塞當前執行緒直到至少一個元素元素入隊或者響應中斷退出。
- 超時退出:當佇列滿時,如果再往佇列中插入元素,阻塞當前執行緒直到佇列中至少一個被移除或者達到指定的等待時間退出或者響應中斷退出;
當佇列為空時,則阻塞當前執行緒直到至少一個元素元素入隊或者達到指定的等待時間退出或者響應中斷退出。
對於每種情況`BlockingQueue`提供的方法如下表:
|方法\處理方式|丟擲異常|返回特定值(布林值)|一直阻塞|超時退出|
|:---:|:---:|:---:|:---:|:---:|
|插入|add(e)|offer(e)|put(e)|offer(e,time,unit)|
|移除|remove()|poll()|take()|poll(time.unit)|
|檢查|element()|peek()|不可用|不可用|
上述方法一般用於生產者-消費者模型中,是其中的生產和消費操作佇列的核心方法。
除了這些方法,`BlockingQueue`還提供了一些其他的方法如下表:
|方法名稱|描述|
|:---:|:---:|
|remove(Object o)|從佇列中移除一個指定值|
|size()|獲取佇列中元素的個數|
|contains(Object o)|判斷佇列是否包含指定的元素,但是這個元素在這次判斷完可能就會被消費|
|drainTo(Collection c)|將佇列中元素放在給定的集合中,並返回新增的元素個數|
|drainTo(Collection c, int maxElements)|將佇列中元素取maxElements(不超過佇列中元素個數)個放在給定的集合中,並返回新增的元素個數|
|remainingCapacity()|計算佇列中還可以存放的元素個數|
|toArray()|以objetc陣列的形式獲取佇列中所有的元素|
|toArray(T[] a)|以給定型別陣列的方式獲取佇列中所有的元素|
|clear()|清空佇列,危險的操作|
##阻塞佇列的實現原理
阻塞佇列的實現依靠通知模式實現:當生產者向滿了的佇列中新增元素時,會阻塞住生產者,
直到消費者消費了一個佇列中的元素後會通知消費者佇列可用,此時再由生產者向佇列中新增元素。反之亦然。
阻塞佇列的阻塞喚醒依靠`Condition`——條件佇列來實現。
以`ArrayBlockingQueue`為例說明:
`ArrayBlockingQueue`的定義:
```java
public class ArrayBlock