本篇主要內(nèi)容如下:
本篇主要內(nèi)容幫你總結(jié)好的阻塞隊列:
18種Queue總結(jié)
一、Queue自我介紹
隊列原理圖1.1 Queue自我介紹
hi,大家好,我的英文名叫Queue
,中文名叫隊列
,無論現(xiàn)實生活中還是計算機的世界中,我都是一個很重要的角色哦~
我是一種數(shù)據(jù)結(jié)構(gòu)
,大家可以把我想象成一個數(shù)組,元素從我的一頭進入、從另外一頭出去,稱為FIFO原則(先進先出原則)。
我還有兩個親兄弟:List
(列表)、Set
(集),他們都是Collection
的兒子,我還有一個遠房親戚:Map
(映射)。他們都是java.util
包這個大家庭的成員哦~
1.2 現(xiàn)實生活中的場景
1.3 計算機世界中的場景
- UDP協(xié)議(接收端將消息存放在隊列中,從隊列中讀取數(shù)據(jù))
二、高屋建瓴,縱覽全局
18種隊列分為三大類: 接口、抽象類、普通類。
弄清楚下面的繼承實現(xiàn)關(guān)系對后面理解18種隊列有很大幫助。
18個Queue的繼承實現(xiàn)關(guān)系圖Queue
接口繼承 Collection
接口,Collection
接口繼承 Iterable
接口BlockingQueue
接口、Deque
接口 繼承 Queue
接口AbstractQueue
抽象類實現(xiàn) Queue
接口BlockingDeque
接口、TransferQueue
接口繼承 BlockingQueue
接口LinkedBlockingDeque
類實現(xiàn) BlockingDeque
接口LinkedTransferQueue
類接口實現(xiàn) TransferQueue
接口LinkedList
類、ArrayDeque
類、ConcurrentLinkedDeque
類實現(xiàn) 了Deque
接口ArrayBlockingQueue
類、LinkendBlockingQueue
類、LinkedBlockingDeque
類、LinkedTransferQueue
類、SynchronousQueue
類、PriorityBlockQueue
類、DelayQueue類
繼承 了AbstractQueue
抽象類和實現(xiàn)了BlockingQueue接口PriorityQueue
類和ConcurrentLinkedQueue
類繼承 了AbstractQueue
抽象類
注意:
- Deque:全稱Double-Ended queue,表示雙端隊列。
三、萬物歸宗Queue接口
2.1 深入理解Queue接口的本質(zhì)
Queue接口是一種Collection,被設(shè)計用于處理之前臨時保存在某處的元素。
除了基本的Collection操作之外,隊列還提供了額外的插入、提取和檢查操作。每一種操作都有兩種形式:如果操作失敗,則拋出一個異常;如果操作失敗,則返回一個特殊值(null或false,取決于是什么操作)。
隊列通常是以FIFO(先進先出)的方式排序元素,但是這不是必須的。
只有優(yōu)先級隊列可以根據(jù)提供的比較器對元素進行排序或者是采用正常的排序。無論怎么排序,隊列的頭將通過調(diào)用remove()或poll()方法進行移除。在FIFO隊列種,所有新的元素被插入到隊尾。其他種類的隊列可能使用不同的布局來存放元素。
2.2 Queue接口的核心方法
總共有3組方法,每一組方法對應(yīng)兩個方法。如下圖所示:
Queue的核心方法動作 | 拋異常 | 返回特殊值 |
---|
Insert | add(e) | offer(e) |
Remove | remove() | poll |
Examine | element() | peek() |
(1)比如添加(Insert)
元素的動作,會有兩種方式:add(e)
和offer(e)
。如果調(diào)用add(e)方法時,添加失敗,則會拋異常
,而如果調(diào)用的是offer(e)方法失敗時,則會返回false
。offer方法用于異常是正常的情況下使用,比如在有界隊列中,優(yōu)先使用offer方法。假如隊列滿了,不能添加元素,offer方法返回false,這樣我們就知道是隊列滿了,而不是去handle運行時拋出的異常。
(2)同理,移除(Remove)元素的動作,隊列為空時,remove方法拋異常,而poll返回null。如果移除頭部的元素成功,則返回移除的元素。
(3)同理,檢測(Examine)元素的動作,返回頭部元素(最開始加入的元素),但不刪除元素, 如果隊列為空,則element()方法拋異常,而peek()返回false。
(4)Queue接口沒有定義阻塞隊列的方法,這些方法在BlockQueue接口中定義了。
(5)Queue實現(xiàn)類通常不允許插入null元素,盡管一些實現(xiàn)類比如LinkedList不禁止插入null,但是還是不建議插入null,因為null也被用在poll方法的特殊返回值,以說明隊列不包含元素。
四、雙端可用Deque接口
4.1 深入理解Deque接口的原理
雙端隊列Deque(1)Deque概念: 支持兩端元素插入和移除的線性集合。名稱deque
是雙端隊列的縮寫,通常發(fā)音為deck
。大多數(shù)實現(xiàn)Deque的類,對它們包含的元素的數(shù)量沒有固定的限制的,支持有界和無界。
(2)Deque方法說明:
Deque方法說明:
該列表包含包含訪問deque兩端元素的方法,提供了插入,移除和檢查元素的方法。
這些方法種的每一種都存在兩種形式:如果操作失敗,則會拋出異常,另一種方法返回一個特殊值(null或false,取決于具體操作)。
插入操作的后一種形式專門設(shè)計用于容量限制的Deque實現(xiàn),大多數(shù)實現(xiàn)中,插入操作不能失敗,所以可以用插入操作的后一種形式。
Deque接口擴展了Queue接口,當(dāng)使用deque作為隊列時,作為FIFO。元素將添加到deque的末尾,并從頭開始刪除。

比如Queue的add方法和Deque的addLast方法等價。
Deque也可以用作LIFO(后進先出)棧,這個接口優(yōu)于傳統(tǒng)的Stack類。當(dāng)作為棧使用時,元素被push到deque隊列的頭,而pop也是從隊列的頭pop出來。
Stack(棧)的方法正好等同于Deque的如下方法:

注意:peek方法不論是作為棧還是隊列,都是從隊列的檢測隊列的頭,返回最先加入的元素。比如第一次put 100,第二次put 200,則peek返回的是100。如下圖所示:
示例代碼4.1 哪些類實現(xiàn)了Deque接口
4.2 哪些類繼承了Deque接口
五、隊列骨架AbstractQueue抽象類
5.1 深入理解AbstractQueue抽象類
AbstractQueue是一個抽象類,繼承了Queue接口,提供了一些Queue操作的骨架實現(xiàn)。
AbstractQueue的方法方法add、remove、element方法基于offer、poll和peek。也就是說如果不能正常操作,則拋出異常。我們來看下AbstactQueue是怎么做到的。
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException('Queue full');
}
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
public E element() {
E x = peek();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
注意:
- 如果繼承AbstractQueue抽象類則必須保證offer方法不允許null值插入。
5.2 哪些類繼承了AbstractQueue抽象類
ArrayBlockingQueue
類、LinkendBlockingQueue
類、LinkedBlockingDeque
類、LinkedTransferQueue
類、SynchronousQueue
類、PriorityBlockQueue
類、DelayQueue類
繼承 了AbstractQueue
抽象類PriorityQueue
類和ConcurrentLinkedQueue
類繼承 了AbstractQueue
抽象類
六、阻塞緩沖BlockingQueue接口
6.1 宏觀來看BlockingQueue(阻塞隊列)
阻塞隊列滿了的情況
阻塞隊列為空的情況(1)BlockingQueue(阻塞隊列)也是一種隊列,支持阻塞的插入和移除方法。
(3)阻塞的插入:當(dāng)隊列滿時,隊列會阻塞插入元素的線程,直到隊列不滿。
(4)阻塞的移除:當(dāng)隊列為空,獲取元素的線程會等待隊列變?yōu)榉强铡?/p>
(5)應(yīng)用場景:生產(chǎn)者和消費者,生產(chǎn)者線程向隊列里添加元素,消費者線程從隊列里移除元素,阻塞隊列時獲取和存放元素的容器。
(6)為什么要用阻塞隊列:生產(chǎn)者生產(chǎn)和消費者消費的速率不一樣,需要用隊列來解決速率差問題,當(dāng)隊列滿了或空的時候,則需要阻塞生產(chǎn)或消費動作來解決隊列滿或空的問題。
6.2 案例解析
線程A往阻塞隊列(Blocking Queue)中添加
元素,而線程B從阻塞隊列中移除
元素。
- 當(dāng)阻塞隊列為空的時候 (一個元素都沒有),則從隊列中獲取元素的操作將會被阻塞。
- 生活中的案例:去海底撈吃火鍋的時候,早上8點沒人來吃火鍋,所以需要等客人過來。
- 翻譯成線程:現(xiàn)在沒有元素需要添加,而且阻塞隊列為空,所以線程B不需要從隊列中拿元素出來,所以線程B獲取元素的操作被阻塞了。
- 當(dāng)阻塞隊列滿了的時候 (所有位置都放有元素),則從隊列中添加元素的操作將會被阻塞。
- 生活中的案例:去海底撈吃火鍋的時候,人太多了,需要排號,等其他桌空出來了才能進去。
- 翻譯成線程:線程A往阻塞隊列中添加元素,將隊列填滿了,線程B現(xiàn)在正在忙,無法拿出隊列中的元素,所以阻塞隊列沒有地方再放元素了,這個時候線程A添加元素的操作就被阻塞了
6.3 操刀BlockingQueue接口
BlockingQueue接口的10個核心方法:
繼承的方法10個核心方法總結(jié)如下:
BlockingQueue接口的10個核心方法有三大類操作:插入、移除、檢查。
- 插入有四種方法: add、offer、put、offer超時版。
IllegalStateException
- 隊列滿了ClassCastException
- 添加的元素類型不匹配NullPointerException
- 添加的元素為nullIllegalArgumentException
- 添加的元素某些屬性不匹配- add方法特別之處用于添加失敗時拋出異常,共有四種異常:
- offer方法特別之處用于添加失敗時只返回false
- put方法特別之處用于當(dāng)阻塞隊列滿時,生產(chǎn)者如果往隊列里put元素,則隊列會一直阻塞生產(chǎn)者線程,直到隊列可用或者響應(yīng)中斷退出
- offer超時方法特別之處用于當(dāng)阻塞隊列滿時,生產(chǎn)者如果往隊列里面插入元素,隊列會阻塞生產(chǎn)者線程一段時間,如果超過了指定時間,生產(chǎn)者線程會退出,并返回false。
- 移除有四種方法: remove、poll、take、poll超時版
NoSuchElementException
- 如果這個隊列是空的- take方法特別之處用于當(dāng)阻塞隊列為空時,消費者線程如果從隊列里面移除元素,則隊列會一直阻塞消費者線程,直到隊列不為空
- poll超時方法特別之處用于當(dāng)阻塞隊列空時,消費者如果從隊列里面刪除元素,則隊列會一直阻塞消費者線程,如果超過了指定時間,消費者線程會退出,并返回null
- element方法用于檢測頭部元素的存在性,如果隊列為空,則拋出異常,否則返回頭部元素。
- peek方法用于檢測頭部元素的存在性,如果隊列為空,返回特殊值null,否則返回頭部的元素。
6.4 BlockingQueue通過什么來阻塞插入和移除的?
- 當(dāng)往隊列里插入一個元素時,如果隊列不可用,那么阻塞生產(chǎn)者主要通過LockSupport. park(this)來實現(xiàn)。
- park這個方法會阻塞當(dāng)前線程,只有以下4種情況中的一種發(fā)生時,該方法才會返回。
- 與park對應(yīng)的unpark執(zhí)行或已經(jīng)執(zhí)行時?!耙呀?jīng)執(zhí)行”是指unpark先執(zhí)行,然后再執(zhí)行park的情況。
- 等待完time參數(shù)指定的毫秒數(shù)時。
- 異?,F(xiàn)象發(fā)生時,這個異常現(xiàn)象沒有任何原因。
6.5 哪些類繼承了BlockingQueue接口?
6.6 哪些類實現(xiàn)了BlockingQueue接口?
- ArrayBlockingQueue類 - 由數(shù)組構(gòu)成的有界阻塞隊列
- LinkedBlockingQueue類 - 由鏈表構(gòu)成的有界阻塞隊列,界限默認大小為Integer.MAX_Value(2^31-1),值非常大,相當(dāng)于無界。
- LinkedBlockingDeque類 - 由鏈表構(gòu)成的雙向阻塞隊列
- LinkedTransferQueue類 - 由鏈表構(gòu)成的無界阻塞隊列
- SynchronousQueue類 - 不存儲元素的阻塞隊列,只有一個元素進行數(shù)據(jù)傳遞。
- LinkedTransferQueue類 - 由鏈表構(gòu)成的無界阻塞TransferQueue隊列
- DelayQueue類 - 使用優(yōu)先級隊列實現(xiàn)的延遲無界阻塞隊列
6.6 BlockingQueue接口繼承了哪些接口
- BlockingQueue接口繼承了Queue接口,可作為隊列使用
七、雙端阻塞BlockingDeque接口
7.1 從原理圖上理解BlockDeque
- BlockQueue滿了,兩端的Take操作被阻塞
BlockingDeque滿了- BlockQueue為空,兩端的Take操作被阻塞
BlockQueue為空7.2 BlockingDeque接口方法
是阻塞隊列BlockingQueue
和雙向隊列Deque
接口的結(jié)合。有如下方法:
BlockingDeque接口方法示例:
嘗試執(zhí)行以下方法:
LinkedBlockingDeque queue = new LinkedBlockingDeque();
queue.addFirst('test1');
queue.addFirst(300);
queue.addLast('400');
最后隊列中的元素順序如下:
300, test1, 400。
先添加了test1放到隊列的頭部,然后在頭部的前面放入300,所以300在最前面,成為頭部,然后將400放入隊列的尾部,所以最后的結(jié)果是300, test1, 400。
隊列種的元素7.3 BlockDeque和BlockQueue的對等方法
BlockDeque和BlockQueue的對等方法7.4 BlockingDeque的特點
7.5 BlockingDeque接口繼承了哪些接口?
- BlockingQueue接口,可作為阻塞隊列使用
7.6 哪些類實現(xiàn)了BlockDeque接口?
八、使命必達TransferQueue接口
8.1 Transfer怎么理解?
如果有消費者正在獲取元素,則將隊列中的元素傳遞給消費者。如果沒有消費者,則等待消費者消費。我把它稱作使命必達隊列,必須將任務(wù)完成才能返回。
8.2 生活中的案例
- 針對TransferQueue的transfer方法
- 圓通快遞員要將小明的2個快遞送貨到門,韻達快遞員也想將小明的2個快遞送貨到門。小明一次只能拿一個,快遞員必須等小明拿了一個后,才能繼續(xù)給第二個。
- 針對TransferQueue的tryTransfer方法
- 圓通快遞員要將小明的2個快遞送貨到門,韻達快遞員也想將小明的2個快遞送貨到門。發(fā)現(xiàn)小明不在家,就把快遞直接放到菜鳥驛站了。
- 針對TransferQueue的tryTransfer超時方法
- 圓通快遞員要將小明的2個快遞送貨到門,韻達快遞員也想將小明的2個快遞送貨到門。發(fā)現(xiàn)小明不在家,于是先等了5分鐘,發(fā)現(xiàn)小明還沒有回來,就把快遞直接放到菜鳥驛站了。
8.3 TransferQueue的原理解析
transfer(E e)
原理如下圖所示:
transfer方法的原理- 原理圖解釋:生產(chǎn)者線程Producer Thread嘗試將元素B傳給消費者線程,如果沒有消費者線程,則將元素B放到尾節(jié)點。并且生產(chǎn)者線程等待元素B被消費。當(dāng)元素B被消費后,生產(chǎn)者線程返回。
- 如果當(dāng)前有消費者正在等待接收元素(消費者通過take方法或超時限制的poll方法時),transfer方法可以把生產(chǎn)者傳入的元素立刻transfer(傳輸)給消費者。
- 如果沒有消費者等待接收元素,transfer方法會將元素放在隊列的tail(尾)節(jié)點,并等到該元素被消費者消費了才返回。
- 試探生產(chǎn)者傳入的元素是否能直接傳給消費者。
- 和transfer方法的區(qū)別是,無論消費者是否接收,方法立即返回。
tryTransfer(E e, long timeout, TimeUnit unit)
- 試圖把生產(chǎn)者傳入的元素直接傳給消費者。
- 如果在超時時間內(nèi)消費了元素,則返回true。
getWaitingConsumerCount()
- 獲取通過BlockingQueue.take()方法或超時限制poll方法等待接受元素的消費者數(shù)量。近似值。
- 獲取是否有通過BlockingQueue.tabke()方法或超時限制poll方法等待接受元素的消費者。
8.3 TransferQueue接口繼承了哪些接口?
- BlockingQueue接口,可作為阻塞隊列使用
8.4 哪些類實現(xiàn)了TransferQueue接口?
九、優(yōu)先由你PriorityQueue類
9.1 理解PriorityQueue類
本應(yīng)該按照升序排序
按照自定義優(yōu)先級排序PriorityQueue是一個支持優(yōu)先級的無界阻塞隊列。
可以通過構(gòu)造參數(shù)Comparator來對元素進行排序。
public PriorityQueue(Comparator<? super E> comparator) {
this(DEFAULT_INITIAL_CAPACITY, comparator);
}
- 自定義實現(xiàn)comapreTo()方法來指定元素排序規(guī)則。
public Comparator<? super E> comparator() {
return comparator;
}
- 實現(xiàn)PriorityQueue接口的類,不保證線程安全,除非是PriorityBlockingQueue。
- PriorityQueue的迭代器不能保證以任何特定順序遍歷元素,如果需要有序遍歷,請考慮使用
Arrays.sort(pq.toArray)
。 - 進列(
offer
、add
)和出列( poll
、remove()
)的時間復(fù)雜度O(log(n))。 - remove(Object) 和 contains(Object)的算法時間復(fù)雜度O(n)。
- peek、element、size的算法時間復(fù)雜度為O(1)。
9.2 PriorityQueue類繼承了哪些類?
9.2 PriorityQueue類實現(xiàn)了哪些接口?
十、雙向鏈表LinkedList類
10.1 LinkedList的結(jié)構(gòu)
- LinkedList實現(xiàn)了List和Deque接口,所以是一種雙鏈表結(jié)構(gòu),可以當(dāng)作堆棧、隊列、雙向隊列使用。
- 一個雙向列表的每一個元素都有三個整數(shù)值:元素、向后的節(jié)點鏈接、向前的節(jié)點鏈接
LinkedList的結(jié)構(gòu)我們來看下節(jié)點類Node
private static class Node<E> {
E item; //元素
Node<E> next; //向后的節(jié)點鏈接
Node<E> prev; //向前的節(jié)點鏈接
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
10.2 與ArrayList的區(qū)別
1.LinkedList的增加和刪除效率相對較高,而查找和修改的效率相對較低。
10.3 LinkedList不是線程安全的
LinkedList不是線程安全的,所以可以使用如下方式保證線程安全。
List list = Collections.synchronizedList(new LinkedList<>());
10.4 LinkedList的家庭成員關(guān)系
LinkedList 繼承了 AbstractSequentialList 類。
LinkedList 實現(xiàn)了 Queue 接口,可作為隊列使用。
LinkedList 繼承了 AbstractQueue抽象類,具有隊列的功能。
LinkedList 實現(xiàn)了 List 接口,可進行列表的相關(guān)操作。
LinkedList 實現(xiàn)了 Deque 接口,可作為雙向隊列使用。
LinkedList 實現(xiàn)了 Cloneable 接口,可實現(xiàn)克隆。
LinkedList 實現(xiàn)了 java.io.Serializable 接口,即可支持序列化,能通過序列化去傳輸。
十一、并發(fā)安全ConcurrentLinkedQueue類
11.1 理解ConcurrentLinkedQueue
ConcurrentLinkedQueue原理- ConcurrentLinked是由鏈表結(jié)構(gòu)組成的線程安全的先進先出無界隊列。
- 當(dāng)多線程要共享訪問集合時,ConcurrentLinkedQueue是一個比較好的選擇。
- 支持非阻塞地訪問并發(fā)安全的隊列,不會拋出ConcurrentModifiationException異常。
- size方法不是準確的,因為在統(tǒng)計集合的時候,隊列可能正在添加元素,導(dǎo)致統(tǒng)計不準。
- 批量操作addAll、removeAll、retainAll、containsAll、equals和toArray不保證原子性(操作不可分割)
- 添加元素happen-before其他線程移除元素。
ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();
BuildingBlockWithName buildingBlock = new BuildingBlockWithName('三角形', 'A');
concurrentLinkedQueue.add(buildingBlock);
11.2 ConcurrentLinkedQueue類繼承了哪些類?
11.3 ConcurrentLinkedQueue類實現(xiàn)了哪些接口?
十二、雙向數(shù)組ArrayDeque類
ArrayDeque原理圖12.1 理解ArrayDeque
- 當(dāng)用作棧時,比棧速度快,當(dāng)用作隊列時,速度比LinkList快。
- remove、removeFirstOccurrence、removeLastOccurrence、contains、remove 和批量操作的算法時間復(fù)雜度O(n)
12.2 使用方法
創(chuàng)建一個ArrayDeque,往arrayDeque隊尾添加元素。
ArrayDeque arrayDeque = new ArrayDeque();
for (int i = 0; i < 50; i++) {
arrayDeque.add(buildingBlock); // add方法等價于addLast方法
}
12.3 ArrayDeque實現(xiàn)了哪些接口
十三、雙向并發(fā)ConcurrentLinkedDeque類
13.1 理解ConcurrentLinkedDeque類
ConcurrentLinkedDeque原理圖- 由鏈表結(jié)構(gòu)組成的雙向無界阻塞隊列
- 插入、刪除和訪問操作可以并發(fā)進行,線程安全的類
- 在并發(fā)場景下,計算隊列的大小是不準確的,因為計算時,可能有元素加入隊列。
- 批量操作addAll、removeAll、retainAll、containsAll、equals和toArray不保證原子性(操作不可分割)
13.2 ConcurrentLinkedDeque使用示例
創(chuàng)建兩個積木:三角形、四邊形,然后添加到隊列:
BuildingBlockWithName buildingBlock1 = new BuildingBlockWithName('三角形', 'A');
BuildingBlockWithName buildingBlock2 = new BuildingBlockWithName('四邊形', 'B');
ConcurrentLinkedDeque concurrentLinkedDeque = new ConcurrentLinkedDeque();
concurrentLinkedDeque.addFirst(buildingBlock1);
concurrentLinkedDeque.addLast(buildingBlock2);
//結(jié)果:順序:三角形、四邊形
13.3 ConcurrentLinkedDeque實現(xiàn)了哪些接口
十四、數(shù)組阻塞ArrayBlockingQueue類
14.1 理解ArrayBlockingQueue
ArrayBlockingQueuey原理圖- ArrayBlockingQueue是一個用數(shù)組實現(xiàn)的有界阻塞隊列。
- 隊列慢時插入操作被阻塞,隊列空時,移除操作被阻塞。
- 公平訪問隊列:按照阻塞的先后順序訪問隊列,即先阻塞的線程先訪問隊列。
- 非公平性是對先等待的線程是非公平的,當(dāng)隊列可用時,阻塞的線程都可以爭奪訪問隊列的資格。有可能先阻塞的線程最后才訪問訪問隊列。
14.2 ArrayBlockingQueue使用示例
創(chuàng)建兩個積木:三角形、四邊形,然后添加到隊列:
BuildingBlockWithName buildingBlock1 = new BuildingBlockWithName('三角形', 'A');
BuildingBlockWithName buildingBlock2 = new BuildingBlockWithName('四邊形', 'B');
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(100, true);
arrayBlockingQueue.add(buildingBlock1);
arrayBlockingQueue.add(buildingBlock2);
14.3 ArrayBlockQueue實現(xiàn)了哪些接口
十五、鏈表阻塞LinkedBlockingQueue類
15.1 理解LinkedBlockingQueue
LinkedBlockingQueue原理- LinkedBlockingQueue具有單鏈表和有界阻塞隊列的功能。
- 隊列慢時插入操作被阻塞,隊列空時,移除操作被阻塞。
- 默認和最大長度為Integer.MAX_VALUE,相當(dāng)于無界(值非常大:2^31-1)。
15.2 LinkedBlockingQueue使用示例
LinkedList linkedList1 = new LinkedList();
linkedList1.add('A');
linkedList1.add('B');
linkedList1.add('C');
15.3 LinkedBlockingQueue的應(yīng)用場景
- 吞吐量通常要高于ArrayBlockingQueue。創(chuàng)建線程池時,參數(shù)runnableTaskQueue(任務(wù)隊列),用于保存等待執(zhí)行的任務(wù)的阻塞隊列可以選擇LinkedBlockingQueue。靜態(tài)工廠方法Executors.newFixedThreadPool()使用了這個隊列。
15.4 LinkedBlockingQueue實現(xiàn)了哪些接口
- LinkedBlockingQueue繼承了 BlockingQueue類,可作為阻塞隊列使用
- LinkedBlockingQueue繼承了 AbstractQueue抽象類,具有隊列的功能。
- LinkedBlockingQueue實現(xiàn)了 java.io.Serializable 接口,即可支持序列化,能通過序列化去傳輸。
十六、雙向阻塞LinkedBlockingDeque類
16.1 理解LinkedBlockingDeque類
LinkedBlockingDeque原理圖- 由鏈LinkedBlockingDeque = 阻塞隊列+鏈表+雙端訪問
- 多線程同時入隊時,因多了一端訪問入口,所以減少了一半的競爭。
- 默認容量大小為Integer.MAX_VALUE。可指定容量大小。
16.2 LinkedBlockingDeque的應(yīng)用場景
LinkedBlockingDeque可以用在“工作竊取“模式中。
工作竊取算法
:某個線程比較空閑,從其他線程的工作隊列中的隊尾竊取任務(wù)來幫忙執(zhí)行。
16.3 LinkedBlockingDeque實現(xiàn)了哪些接口
- LinkedBlockingDeque繼承了 BlockingDeque類,可作為阻塞隊列使用
- LinkedBlockingDeque繼承了 AbstractQueue抽象類,具有隊列的功能。
- LinkedBlockingDeque實現(xiàn)了 java.io.Serializable 接口,即可支持序列化,能通過序列化去傳輸。
十七、鏈表阻塞LinkedTransferQueue類
17.1 理解LinkedTransferQueue類
LinkedTransferQueue原理圖LinkedTransferQueue = 阻塞隊列+鏈表結(jié)構(gòu)+TransferQueue
之前我們講“使命必達TransferQueue接口時已經(jīng)介紹過了TransferQueue接口 ,所以LinkedTransferQueue接口跟它相似,只是加入了阻塞插入和移除的功能,以及結(jié)構(gòu)是鏈表結(jié)構(gòu)。
之前的TransferQueue也講到了3個案例來說明TransferQueue的原理,大家可以回看TransferQueue。
17.2 LinkedTransferQueue接口比其他阻塞隊列多了5個方法
- tryTransfer(E e, long timeout, TimeUnit unit)
- getWaitingConsumerCount()
17.3 LinkedTransferQueue代碼示例
- 創(chuàng)建一個LinkedTransferQueue,生產(chǎn)者1 依次往隊列中添加 A、B、C
生產(chǎn)者1 依次往隊列中添加 A、B、C- 生產(chǎn)者2 依次往隊列中添加 D、E、F
生產(chǎn)者2 依次往隊列中添加 D、E、F- 消費者依次從隊列首部開始消費元素,每次消費前,先sleep 2s,來演示transfer方法是否進行了等待。
消費者消費元素生產(chǎn)者1 transfer A
生產(chǎn)者2 transfer D
2s后...
消費者 take A
生產(chǎn)者1 put B
2s后...
消費者 take D
生產(chǎn)者2 transfer E
2s后...
消費者 take B
生產(chǎn)者1 transfer C
(1)首先生產(chǎn)者線程1和2 調(diào)用transfer方法來傳輸A和D,發(fā)現(xiàn)沒有消費者線程接收,所以被阻塞。
(2)消費者線程過了2s后將A拿走了,然后生產(chǎn)者1 被釋放繼續(xù)執(zhí)行,傳輸元素B,發(fā)現(xiàn)又沒有消費者消費,所以進行了等待。
(3)消費者線程過了2s后,將排在隊列首部的D元素拿走,生產(chǎn)者2繼續(xù)往下執(zhí)行,傳輸元素E,發(fā)現(xiàn)沒有消費者,所以進行了等待。
(4)消費者線程過了2s后,將排在隊列首部的B元素拿走,生產(chǎn)者1傳輸C元素,等待消費者拿走。
(5)消費者不再消費了,所以生產(chǎn)者1和生產(chǎn)者2都被阻塞了,元素C和,元素E都沒有被拿走,而且生產(chǎn)者2的F元素還沒有開始傳輸,因為在等待元素D被拿走。
(6)看下隊列里面確實有C和E元素,而且E排在隊列的首部。
隊列里面的元素17.4 LinkedTransferQueue實現(xiàn)了哪些接口
- LinkedBlockingDeque繼承了 BlockingQeque類,可作為阻塞隊列使用
- LinkedBlockingDeque繼承了 AbstractQueue抽象類,具有隊列的功能。
十八、傳球好手SynchronousQueue類
18.1 理解SynchronousQueue類
SynchronousQueue原理圖我稱SynchronousQueue為”傳球好手“。想象一下這個場景:小明抱著一個籃球想傳給小花,如果小花沒有將球拿走,則小明是不能再拿其他球的。
SynchronousQueue負責(zé)把生產(chǎn)者產(chǎn)生的數(shù)據(jù)傳遞給消費者線程。
SynchronousQueue本身不存儲數(shù)據(jù),調(diào)用了put方法后,隊列里面也是空的。
每一個put操作必須等待一個take操作完成,否則不能添加元素。
性能高于ArrayBlockingQueue和LinkedBlockingQueue。
18.2 SynchronousQueue示例
我們創(chuàng)建了兩個線程,一個線程用于生產(chǎn),一個線程用于消費
- 生產(chǎn)的線程依次put A、B、C三個值
生產(chǎn)的線程依次put A、B、C三個值- 消費線程使用take來消費阻塞隊列中的內(nèi)容,每次消費前,等待5秒
消費線程每隔5s調(diào)用take方法t1 put A
t2 take A
5秒后...
t1 put B
t2 take B
5秒后...
t1 put C
t2 take C
小結(jié):說明生產(chǎn)線程執(zhí)行put第一個元素'A' 操作后,需要等待消費者線程take完“A”后,才能繼續(xù)往下執(zhí)行代碼。
18.1 SynchronousQueue應(yīng)用場景
- 吞吐量通常要高于LinkedBlockingQueue。創(chuàng)建線程池時,參數(shù)runnableTaskQueue(任務(wù)隊列),用于保存等待執(zhí)行的任務(wù)的阻塞隊列可以選擇SynchronousQueue。靜態(tài)工廠方法Executors.newCachedThreadPool()使用了這個隊列
18.2 SynchronousQueue和LinkedTransferQueue的區(qū)別
- SynchronousQueue 不存儲元素,而LinkedTransferQueue存儲元素。
- SynchronousQueue 隊列里面沒有元素,而LinkedTransferQueue可以有多個存儲在隊列等待傳輸。
- LinkedTransferQueue還支持若傳輸不了,則丟到隊列里面去。
- LinkedTransferQueue還支持若超過一定時間傳輸不了,則丟到隊列里面去。
十九、優(yōu)先級阻塞PriorityBlockingQueue類
19.1 理解PriorityBlockQueue類
PriorityBlockQueue的原理圖- PriorityBlockQueue = PriorityQueue + BlockingQueue
- 之前我們也講到了PriorityQueue的原理,支持對元素排序。
- 可以自定義CompareTo()方法來指定元素排序規(guī)則。
- 可以通過構(gòu)造函數(shù)構(gòu)造參數(shù)Comparator來對元素進行排序。
19.2 PriorityBlockQueue實現(xiàn)了哪些接口
- LinkedBlockingQueue繼承了 BlockingQueue接口,可作為阻塞隊列使用
- LinkedBlockingQueue繼承了 AbstractQueue抽象類,具有隊列的功能。
- LinkedBlockingQueue實現(xiàn)了 java.io.Serializable 接口,即可支持序列化,能通過序列化去傳輸。
二十、延時阻塞DelayQueue類
20.1 理解DelayQueue
DelayQueue原理圖- DelayQueue = Delayed + BlockingQueue。隊列中的元素必須實現(xiàn)Delayed接口。
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
- 在創(chuàng)建元素時,可以指定多久可以從隊列中獲取到當(dāng)前元素。只有在延時期滿才能從隊列中獲取到當(dāng)前元素。
20.2 源碼解析
public boolean offer(E e, long timeout, TimeUnit unit) {
return offer(e);
}
- 獲取元素的方法poll需要等待延時時間過了才能獲取到元素
if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
return q.poll();
poll方法20.3 應(yīng)用場景
緩存系統(tǒng)的設(shè)計:可以用DelayQueue保存緩存元素的有效期。然后用一個線程循環(huán)的查詢DelayQueue隊列,一旦能從DelayQueue中獲取元素時,表示緩存有效期到了。
定時任務(wù)調(diào)度:使用DelayQueue隊列保存當(dāng)天將會執(zhí)行的任務(wù)和執(zhí)行時間,一旦從DelayQueue中獲取到任務(wù)就開始執(zhí)行。比如Java中的TimerQueue就是使用DelayQueue實現(xiàn)的。
20.4 DelayQueue實現(xiàn)了哪些接口
- DelayQueue實現(xiàn)了 BlockingQueue接口,可作為阻塞隊列使用
這一篇花了很多心思在上面,看官方英文文檔、畫原理圖、寫demo代碼,排版。這恐怕是市面上最全最細講解Queue的。