本文已收錄至 https://github.com/vipstone/algorithm 《算法圖解》系列。
通過前面文章的學習《一文詳解「隊列」,手撸隊列的3種方法!》我們知道了隊列(Queue)是先進先出(FIFO)的,并且我們可以用數組、連結清單還有 List 的方式來實作自定義隊列,那麼本文我們來系統的學習一下官方是如何實作隊列的。
Java 中的隊列有很多,例如:<code>ArrayBlockingQueue</code>、<code>LinkedBlockingQueue</code>、<code>PriorityQueue</code>、<code>DelayQueue</code>、<code>SynchronousQueue</code> 等,那它們的作用是什麼?又是如何分類的呢?
其實 Java 中的這些隊列可以從不同的次元進行分類,例如可以從阻塞和非阻塞進行分類,也可以從有界和無界進行分類,而本文将從隊列的功能上進行分類,例如:優先隊列、普通隊列、雙端隊列、延遲隊列等。
雖然本文的重點是從功能上對隊列進行解讀,但其它分類也是 Java 中的重要概念,是以我們先來了解一下它們。
阻塞隊列(Blocking Queue)提供了可阻塞的 <code>put</code> 和 <code>take</code> 方法,它們與可定時的 <code>offer</code> 和 <code>poll</code> 是等價的。如果隊列滿了 <code>put</code> 方法會被阻塞等到有空間可用再将元素插入;如果隊列是空的,那麼 <code>take</code> 方法也會阻塞,直到有元素可用。當隊列永遠不會被充滿時,<code>put</code> 方法和 <code>take</code> 方法就永遠不會阻塞。
我們可以從隊列的名稱中知道此隊列是否為阻塞隊列,阻塞隊列中包含 <code>BlockingQueue</code> 關鍵字,比如以下這些:
ArrayBlockingQueue
LinkedBlockingQueue
PriorityBlockingQueue
.......
接下來我們來示範一下當阻塞隊列的容量滿了之後會怎樣,示例代碼如下:
以上代碼的執行結果如下:
Mon Oct 19 20:16:12 CST 2020 | ArrayBlockingQueue Size:1 Mon Oct 19 20:16:12 CST 2020 | ArrayBlockingQueue Size:2 Mon Oct 19 20:16:12 CST 2020 | ArrayBlockingQueue Size:3 Mon Oct 19 20:16:12 CST 2020 | ArrayBlockingQueue Size:4 Mon Oct 19 20:16:12 CST 2020 | ArrayBlockingQueue Size:5 Mon Oct 19 20:16:13 CST 2020 | ArrayBlockingQueue Size:5 Mon Oct 19 20:16:14 CST 2020 | ArrayBlockingQueue Size:5 Mon Oct 19 20:16:15 CST 2020 | ArrayBlockingQueue Size:5 Mon Oct 19 20:16:16 CST 2020 | ArrayBlockingQueue Size:5 Mon Oct 19 20:16:17 CST 2020 | ArrayBlockingQueue Size:5 Mon Oct 19 20:16:17 CST 2020 | For End.
從上述結果可以看出,當 <code>ArrayBlockingQueue</code> 隊列滿了之後就會進入阻塞,當過了 1 秒有元素從隊列中移除之後,才會将新的元素入列。
非阻塞隊列也就是普通隊列,它的名字中不會包含 <code>BlockingQueue</code> 關鍵字,并且它不會包含 <code>put</code> 和 <code>take</code> 方法,當隊列滿之後如果還有新元素入列會直接傳回錯誤,并不會阻塞的等待着添加元素,如下圖所示:
非阻塞隊列的典型代表是 <code>ConcurrentLinkedQueue</code> 和 <code>PriorityQueue</code>。
有界隊列:是指有固定大小的隊列,比如設定了固定大小的 <code>ArrayBlockingQueue</code>,又或者大小為 0 的 <code>SynchronousQueue</code>。
無界隊列:指的是沒有設定固定大小的隊列,但其實如果沒有設定固定大小也是有預設值的,隻不過預設值是 Integer.MAX_VALUE,當然實際的使用中不會有這麼大的容量(超過 Integer.MAX_VALUE),是以從使用者的角度來看相當于 “無界”的。
接下來就是本文的重點了,我們以功能來劃分一下隊列,它可以被分為:普通隊列、優先隊列、雙端隊列、延遲隊列、其他隊列等,接下來我們分别來看。
普通隊列(Queue)是指實作了先進先出的基本隊列,例如 <code>ArrayBlockingQueue</code> 和 <code>LinkedBlockingQueue</code>,其中 <code>ArrayBlockingQueue</code> 是用數組實作的普通隊列,如下圖所示:
而 <code>LinkedBlockingQueue</code> 是使用連結清單實作的普通隊列,如下圖所示:
普通隊列中的常用方法有以下這些:
offer():添加元素,如果隊列已滿直接傳回 false,隊列未滿則直接插入并傳回 true;
poll():删除并傳回隊頭元素,當隊列為空傳回 null;
add():添加元素,此方法是對 offer 方法的簡單封裝,如果隊列已滿,抛出 IllegalStateException 異常;
remove():直接删除隊頭元素;
put():添加元素,如果隊列已經滿,則會阻塞等待插入;
take():删除并傳回隊頭元素,當隊列為空,則會阻塞等待;
peek():查詢隊頭元素,但不會進行删除;
element():對 peek 方法進行簡單封裝,如果隊頭元素存在則取出并不删除,如果不存在抛出 NoSuchElementException 異常。
注意:一般情況下 offer() 和 poll() 方法配合使用,put() 和 take() 阻塞方法配合使用,add() 和 remove() 方法會配合使用,程式中常用的是 offer() 和 poll() 方法,是以這兩個方法比較友好,不會報錯。
接下來我們以 <code>LinkedBlockingQueue</code> 為例,示範一下普通隊列的使用:
Hello Java 中文社群
雙端隊列(Deque)是指隊列的頭部和尾部都可以同時入隊和出隊的資料結構,如下圖所示:
接下來我們來示範一下雙端隊列 <code>LinkedBlockingDeque</code> 的使用:
offerFirst offer offerLast
優先隊列(PriorityQueue)是一種特殊的隊列,它并不是先進先出的,而是優先級高的元素先出隊。
優先隊列是根據二叉堆實作的,二叉堆的資料結構如下圖所示:
二叉堆分為兩種類型:一種是最大堆一種是最小堆。以上展示的是最大堆,在最大堆中,任意一個父節點的值都大于等于它左右子節點的值。
因為優先隊列是基于二叉堆實作的,是以它可以将優先級最好的元素先出隊。
接下來我們來示範一下優先隊列的使用:
Name:MySQL Level:5 Name:Redis Level:3 Name:Java Level:1
從上述結果可以看出,優先隊列的出隊是不考慮入隊順序的,它始終遵循的是優先級高的元素先出隊。
延遲隊列(DelayQueue)是基于優先隊列 <code>PriorityQueue</code> 實作的,它可以看作是一種以時間為度量機關的優先的隊列,當入隊的元素到達指定的延遲時間之後方可出隊。
我們來示範一下延遲隊列的使用:
開始執行時間:2020-10-20 20:17:28 消息1 消息2 結束執行時間:2020-10-20 20:17:31
從上述結束執行時間和開始執行時間可以看出,消息 1 和消息 2 都正常實作了延遲執行的功能。
在 Java 的隊列中有一個比較特殊的隊列 <code>SynchronousQueue</code>,它的特别之處在于它内部沒有容器,每次進行 <code>put()</code> 資料後(添加資料),必須等待另一個線程拿走資料後才可以再次添加資料,它的使用示例如下:
Mon Oct 19 21:00:21 CST 2020,元素入隊 Mon Oct 19 21:00:22 CST 2020,元素出隊:Data 0 Mon Oct 19 21:00:22 CST 2020,元素入隊 Mon Oct 19 21:00:23 CST 2020,元素出隊:Data 1 Mon Oct 19 21:00:23 CST 2020,元素入隊 Mon Oct 19 21:00:24 CST 2020,元素出隊:Data 2
從上述結果可以看出,當有一個元素入隊之後,隻有等到另一個線程将元素出隊之後,新的元素才能再次入隊。
本文講了 Java 中的 5 種隊列:普通隊列、雙端隊列、優先隊列、延遲隊列、其他隊列。其中普通隊列的典型代表為 <code>ArrayBlockingQueue</code> 和 <code>LinkedBlockingQueue</code>,雙端隊列的代表為 <code>LinkedBlockingDeque</code>,優先隊列的代表為 <code>PriorityQueue</code>,延遲隊列的代表為 <code>DelayQueue</code>,最後還講了内部沒有容器的其他隊列 <code>SynchronousQueue</code>。
關注下面二維碼,訂閱更多精彩内容。

關注公衆号(加好友):
作者:
王磊的部落格
出處:
http://vipstone.cnblogs.com/