Java中的5大隊(duì)列,你知道幾個(gè)?
我們知道隊(duì)列(Queue)是先進(jìn)先出(FIFO)的,并且我們可以用數(shù)組、鏈表還有 List 的方式來(lái)實(shí)現(xiàn)自定義隊(duì)列,那么本文我們來(lái)系統(tǒng)的學(xué)習(xí)一下官方是如何實(shí)現(xiàn)隊(duì)列的。

阻塞隊(duì)列和非阻塞隊(duì)列
put 和 take 方法,它們與可定時(shí)的 offer 和 poll 是等價(jià)的。如果隊(duì)列滿(mǎn)了 put 方法會(huì)被阻塞等到有空間可用再將元素插入;如果隊(duì)列是空的,那么 take 方法也會(huì)阻塞,直到有元素可用。當(dāng)隊(duì)列永遠(yuǎn)不會(huì)被充滿(mǎn)時(shí),put 方法和 take?方法就永遠(yuǎn)不會(huì)阻塞。
BlockingQueue?關(guān)鍵字,比如以下這些:ArrayBlockingQueue LinkedBlockingQueue PriorityBlockingQueue .......
阻塞隊(duì)列功能演示
import?java.util.Date;
import?java.util.concurrent.ArrayBlockingQueue;
public?class?BlockingTest?{
????public?static?void?main(String[]?args)?throws?InterruptedException?{
????????//?創(chuàng)建一個(gè)長(zhǎng)度為?5?的阻塞隊(duì)列
????????ArrayBlockingQueue?q1?=?new?ArrayBlockingQueue(5);
????????
????????//?新創(chuàng)建一個(gè)線(xiàn)程執(zhí)行入列
????????new?Thread(()?->?{
????????????//?循環(huán)?10?次
????????????for?(int?i?=?0;?i?10;?i++)?{
????????????????try?{
????????????????????q1.put(i);
????????????????}?catch?(InterruptedException?e)?{
????????????????????e.printStackTrace();
????????????????}
????????????????System.out.println(new?Date()?+?"?|?ArrayBlockingQueue?Size:"?+?q1.size());
????????????}
????????????System.out.println(new?Date()?+?"?|?For?End.");
????????}).start();
????????//?新創(chuàng)建一個(gè)線(xiàn)程執(zhí)行出列
????????new?Thread(()?->?{
????????????for?(int?i?=?0;?i?5;?i++)?{
????????????????try?{
????????????????????//?休眠?1S
????????????????????Thread.sleep(1000);
????????????????}?catch?(InterruptedException?e)?{
????????????????????e.printStackTrace();
????????????????}
????????????????if?(!q1.isEmpty())?{
????????????????????try?{
????????????????????????q1.take();?//?出列
????????????????????}?catch?(InterruptedException?e)?{
????????????????????????e.printStackTrace();
????????????????????}
????????????????}
????????????}
????????}).start();
????}
}
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.
ArrayBlockingQueue?隊(duì)列滿(mǎn)了之后就會(huì)進(jìn)入阻塞,當(dāng)過(guò)了 1 秒有元素從隊(duì)列中移除之后,才會(huì)將新的元素入列。非阻塞隊(duì)列
BlockingQueue?關(guān)鍵字,并且它不會(huì)包含 put?和 take?方法,當(dāng)隊(duì)列滿(mǎn)之后如果還有新元素入列會(huì)直接返回錯(cuò)誤,并不會(huì)阻塞的等待著添加元素,如下圖所示:
ConcurrentLinkedQueue?和 PriorityQueue。有界隊(duì)列和無(wú)界隊(duì)列
ArrayBlockingQueue,又或者大小為 0 的 SynchronousQueue。

按功能分類(lèi)
1.普通隊(duì)列
ArrayBlockingQueue?和 LinkedBlockingQueue,其中 ArrayBlockingQueue 是用數(shù)組實(shí)現(xiàn)的普通隊(duì)列,如下圖所示:
LinkedBlockingQueue 是使用鏈表實(shí)現(xiàn)的普通隊(duì)列,如下圖所示:
常用方法
offer():添加元素,如果隊(duì)列已滿(mǎn)直接返回 false,隊(duì)列未滿(mǎn)則直接插入并返回 true; poll():刪除并返回隊(duì)頭元素,當(dāng)隊(duì)列為空返回 null; add():添加元素,此方法是對(duì) offer 方法的簡(jiǎn)單封裝,如果隊(duì)列已滿(mǎn),拋出 IllegalStateException 異常; remove():直接刪除隊(duì)頭元素; put():添加元素,如果隊(duì)列已經(jīng)滿(mǎn),則會(huì)阻塞等待插入; take():刪除并返回隊(duì)頭元素,當(dāng)隊(duì)列為空,則會(huì)阻塞等待; peek():查詢(xún)隊(duì)頭元素,但不會(huì)進(jìn)行刪除; element():對(duì) peek 方法進(jìn)行簡(jiǎn)單封裝,如果隊(duì)頭元素存在則取出并不刪除,如果不存在拋出 NoSuchElementException 異常。
LinkedBlockingQueue 為例,演示一下普通隊(duì)列的使用:import?java.util.concurrent.LinkedBlockingQueue;
static?class?LinkedBlockingQueueTest?{
????public?static?void?main(String[]?args)?{
????????LinkedBlockingQueue?queue?=?new?LinkedBlockingQueue();
????????queue.offer("Hello");
????????queue.offer("Java");
????????queue.offer("AAA");
????????while?(!queue.isEmpty())?{
????????????System.out.println(queue.poll());
????????}
????}
}
Hello
Java
AAA
2.雙端隊(duì)列

LinkedBlockingDeque 的使用:import?java.util.concurrent.LinkedBlockingDeque;
/**
??*?雙端隊(duì)列示例
??*/
static?class?LinkedBlockingDequeTest?{
????public?static?void?main(String[]?args)?{
????????//?創(chuàng)建一個(gè)雙端隊(duì)列
????????LinkedBlockingDeque?deque?=?new?LinkedBlockingDeque();
????????deque.offer("offer");?//?插入首個(gè)元素
????????deque.offerFirst("offerFirst");?//?隊(duì)頭插入元素
????????deque.offerLast("offerLast");?//?隊(duì)尾插入元素
????????while?(!deque.isEmpty())?{
????????????//?從頭遍歷打印
????????????System.out.println(deque.poll());
????????}
????}
}
offerFirst offer offerLast
3.優(yōu)先隊(duì)列

因?yàn)閮?yōu)先隊(duì)列是基于二叉堆實(shí)現(xiàn)的,因此它可以將優(yōu)先級(jí)最好的元素先出隊(duì)。
import?java.util.PriorityQueue;
public?class?PriorityQueueTest?{
????//?自定義的實(shí)體類(lèi)
????static?class?Viper?{
????????private?int?id;?//?id
????????private?String?name;?//?名稱(chēng)
????????private?int?level;?//?等級(jí)
????????public?Viper(int?id,?String?name,?int?level)?{
????????????this.id?=?id;
????????????this.name?=?name;
????????????this.level?=?level;
????????}
????????public?int?getId()?{
????????????return?id;
????????}
????????public?void?setId(int?id)?{
????????????this.id?=?id;
????????}
????????public?String?getName()?{
????????????return?name;
????????}
????????public?void?setName(String?name)?{
????????????this.name?=?name;
????????}
????????public?int?getLevel()?{
????????????return?level;
????????}
????????public?void?setLevel(int?level)?{
????????????this.level?=?level;
????????}
????}
????public?static?void?main(String[]?args)?{
??PriorityQueue?queue?=?new?PriorityQueue(10,?new?Comparator()?{
????????????@Override
????????????public?int?compare(Viper?v1,?Viper?v2)?{
????????????????//?設(shè)置優(yōu)先級(jí)規(guī)則(倒序,等級(jí)越高權(quán)限越大)
????????????????return?v2.getLevel()?-?v1.getLevel();
????????????}
????????});
????????//?構(gòu)建實(shí)體類(lèi)
????????Viper?v1?=?new?Viper(1,?"Java",?1);
????????Viper?v2?=?new?Viper(2,?"MySQL",?5);
????????Viper?v3?=?new?Viper(3,?"Redis",?3);
????????//?入列
????????queue.offer(v1);
????????queue.offer(v2);
????????queue.offer(v3);
????????while?(!queue.isEmpty())?{
????????????//?遍歷名稱(chēng)
????????????Viper?item?=?(Viper)?queue.poll();
????????????System.out.println("Name:"?+?item.getName()?+
???????????????????????????????" Level:"?+?item.getLevel());
????????}
????}
}
Name:MySQL Level:5
Name:Redis Level:3
Name:Java Level:1
4.延遲隊(duì)列
PriorityQueue 實(shí)現(xiàn)的,它可以看作是一種以時(shí)間為度量單位的優(yōu)先的隊(duì)列,當(dāng)入隊(duì)的元素到達(dá)指定的延遲時(shí)間之后方可出隊(duì)。
我們來(lái)演示一下延遲隊(duì)列的使用:
import?lombok.Getter;
import?lombok.Setter;
import?java.text.DateFormat;
import?java.util.Date;
import?java.util.concurrent.DelayQueue;
import?java.util.concurrent.Delayed;
import?java.util.concurrent.TimeUnit;
public?class?CustomDelayQueue?{
????//?延遲消息隊(duì)列
????private?static?DelayQueue?delayQueue?=?new?DelayQueue();
????public?static?void?main(String[]?args)?throws?InterruptedException?{
????????producer();?//?調(diào)用生產(chǎn)者
????????consumer();?//?調(diào)用消費(fèi)者
????}
????//?生產(chǎn)者
????public?static?void?producer()?{
????????//?添加消息
????????delayQueue.put(new?MyDelay(1000,?"消息1"));
????????delayQueue.put(new?MyDelay(3000,?"消息2"));
????}
????//?消費(fèi)者
????public?static?void?consumer()?throws?InterruptedException?{
????????System.out.println("開(kāi)始執(zhí)行時(shí)間:"?+
????????????????DateFormat.getDateTimeInstance().format(new?Date()));
????????while?(!delayQueue.isEmpty())?{
????????????System.out.println(delayQueue.take());
????????}
????????System.out.println("結(jié)束執(zhí)行時(shí)間:"?+
????????????????DateFormat.getDateTimeInstance().format(new?Date()));
????}
????static?class?MyDelay?implements?Delayed?{
????????//?延遲截止時(shí)間(單位:毫秒)
????????long?delayTime?=?System.currentTimeMillis();
????????//?借助?lombok?實(shí)現(xiàn)
????????@Getter
????????@Setter
????????private?String?msg;
????????/**
?????????*?初始化
?????????*?@param?delayTime?設(shè)置延遲執(zhí)行時(shí)間
?????????*?@param?msg???????執(zhí)行的消息
?????????*/
????????public?MyDelay(long?delayTime,?String?msg)?{
????????????this.delayTime?=?(this.delayTime?+?delayTime);
????????????this.msg?=?msg;
????????}
????????//?獲取剩余時(shí)間
????????@Override
????????public?long?getDelay(TimeUnit?unit)?{
????????????return?unit.convert(delayTime?-?System.currentTimeMillis(),?TimeUnit.MILLISECONDS);
????????}
????????//?隊(duì)列里元素的排序依據(jù)
????????@Override
????????public?int?compareTo(Delayed?o)?{
????????????if?(this.getDelay(TimeUnit.MILLISECONDS)?>?o.getDelay(TimeUnit.MILLISECONDS))?{
????????????????return?1;
????????????}?else?if?(this.getDelay(TimeUnit.MILLISECONDS)?????????????????return?-1;
????????????}?else?{
????????????????return?0;
????????????}
????????}
????????@Override
????????public?String?toString()?{
????????????return?this.msg;
????????}
????}
}
開(kāi)始執(zhí)行時(shí)間:2020-10-20 20:17:28
消息1
消息2
結(jié)束執(zhí)行時(shí)間:2020-10-20 20:17:31
5.其他隊(duì)列
SynchronousQueue,它的特別之處在于它內(nèi)部沒(méi)有容器,每次進(jìn)行 put() 數(shù)據(jù)后(添加數(shù)據(jù)),必須等待另一個(gè)線(xiàn)程拿走數(shù)據(jù)后才可以再次添加數(shù)據(jù),它的使用示例如下:import?java.util.concurrent.SynchronousQueue;
public?class?SynchronousQueueTest?{
????public?static?void?main(String[]?args)?{
????????SynchronousQueue?queue?=?new?SynchronousQueue();
????????//?入隊(duì)
????????new?Thread(()?->?{
????????????for?(int?i?=?0;?i?3;?i++)?{
????????????????try?{
????????????????????System.out.println(new?Date()?+?",元素入隊(duì)");
????????????????????queue.put("Data?"?+?i);
????????????????}?catch?(InterruptedException?e)?{
????????????????????e.printStackTrace();
????????????????}
????????????}
????????}).start();
????????//?出隊(duì)
????????new?Thread(()?->?{
????????????while?(true)?{
????????????????try?{
????????????????????Thread.sleep(1000);
????????????????????System.out.println(new?Date()?+?",元素出隊(duì):"?+?queue.take());
????????????????}?catch?(InterruptedException?e)?{
????????????????????e.printStackTrace();
????????????????}
????????????}
????????}).start();
????}
}
Mon Oct 19 21:00:21 CST 2020,元素入隊(duì)
Mon Oct 19 21:00:22 CST 2020,元素出隊(duì):Data 0
Mon Oct 19 21:00:22 CST 2020,元素入隊(duì)
Mon Oct 19 21:00:23 CST 2020,元素出隊(duì):Data 1
Mon Oct 19 21:00:23 CST 2020,元素入隊(duì)
Mon Oct 19 21:00:24 CST 2020,元素出隊(duì):Data 2
總結(jié)
ArrayBlockingQueue 和 LinkedBlockingQueue,雙端隊(duì)列的代表為 LinkedBlockingDeque,優(yōu)先隊(duì)列的代表為 PriorityQueue,延遲隊(duì)列的代表為 DelayQueue,最后還講了內(nèi)部沒(méi)有容器的其他隊(duì)列 SynchronousQueue。