摘要:是基于鏈接節(jié)點(diǎn)的線程安全的隊(duì)列。通過(guò)這些高效并且線程安全的隊(duì)列類,為我們快速搭建高質(zhì)量的多線程程序帶來(lái)極大的便利。隊(duì)列內(nèi)部?jī)H允許容納一個(gè)元素。該隊(duì)列的頭部是延遲期滿后保存時(shí)間最長(zhǎng)的元素。
隊(duì)列簡(jiǎn)述
Queue: 基本上,一個(gè)隊(duì)列就是一個(gè)先入先出(FIFO)的數(shù)據(jù)結(jié)構(gòu)
Queue接口與List、Set同一級(jí)別,都是繼承了Collection接口。LinkedList實(shí)現(xiàn)了Deque接 口。
在并發(fā)隊(duì)列上JDK提供了兩套實(shí)現(xiàn),一個(gè)是以ConcurrentLinkedQueue為代表的高性能隊(duì)列非阻塞,一個(gè)是以BlockingQueue接口為代表的阻塞隊(duì)列,無(wú)論哪種都繼承自Queue
阻塞隊(duì)列與普通隊(duì)列的區(qū)別在于,當(dāng)隊(duì)列是空的時(shí),從隊(duì)列中獲取元素的操作將會(huì)被阻塞,或者當(dāng)隊(duì)列是滿時(shí),往隊(duì)列里添加元素的操作會(huì)被阻塞。試圖從空的阻塞隊(duì)列中獲取元素的線程將會(huì)被阻塞,直到其他的線程往空的隊(duì)列插入新的元素。同樣,試圖往已滿的阻塞隊(duì)列中添加新元素的線程同樣也會(huì)被阻塞,直到其他的線程使隊(duì)列重新變得空閑起來(lái),如從隊(duì)列中移除一個(gè)或者多個(gè)元素,或者完全清空隊(duì)列
(圖片來(lái)自網(wǎng)絡(luò)https://www.cnblogs.com/lemon...)
沒(méi)有實(shí)現(xiàn)的阻塞接口的LinkedList: 實(shí)現(xiàn)了java.util.Queue接口和java.util.AbstractQueue接口
內(nèi)置的不阻塞隊(duì)列: PriorityQueue 和 ConcurrentLinkedQueue
PriorityQueue 和 ConcurrentLinkedQueue 類在 Collection Framework 中加入兩個(gè)具體集合實(shí)現(xiàn)。
PriorityQueue 類實(shí)質(zhì)上維護(hù)了一個(gè)有序列表。加入到 Queue 中的元素根據(jù)它們的天然排序(通過(guò)其 java.util.Comparable 實(shí)現(xiàn))或者根據(jù)傳遞給構(gòu)造函數(shù)的 java.util.Comparator 實(shí)現(xiàn)來(lái)定位。
ConcurrentLinkedQueue 是基于鏈接節(jié)點(diǎn)的、線程安全的隊(duì)列。并發(fā)訪問(wèn)不需要同步。因?yàn)樗陉?duì)列的尾部添加元素并從頭部刪除它們,所以只要不需要知道隊(duì)列的大小,ConcurrentLinkedQueue 對(duì)公共集合的共享訪問(wèn)就可以工作得很好。收集關(guān)于隊(duì)列大小的信息會(huì)很慢,需要遍歷隊(duì)列。
ConcurrentLinkedQueue : 是一個(gè)適用于高并發(fā)場(chǎng)景下的隊(duì)列,通過(guò)無(wú)鎖的方式,實(shí)現(xiàn)
了高并發(fā)狀態(tài)下的高性能,通常ConcurrentLinkedQueue性能好于BlockingQueue.它
是一個(gè)基于鏈接節(jié)點(diǎn)的無(wú)界線程安全隊(duì)列。該隊(duì)列的元素遵循先進(jìn)先出的原則。頭是最先
加入的,尾是最近加入的,該隊(duì)列不允許null元素。
ConcurrentLinkedQueue重要方法:
add 和offer() 都是加入元素的方法(在ConcurrentLinkedQueue中這倆個(gè)方法沒(méi)有任何區(qū)別)
poll() 和peek() 都是取頭元素節(jié)點(diǎn),區(qū)別在于前者會(huì)刪除元素,后者不會(huì)。
@RequestMapping("test-clq") public void testConcurrentLinkedQueue() { ConcurrentLinkedDequeq = new ConcurrentLinkedDeque<>(); q.offer("Java"); q.offer("C#"); q.offer("Javascript"); q.offer("Python"); // 從頭獲取元素,刪除該元素 System.out.println(q.poll()); // 從頭獲取元素,不刪除該元素 System.out.println(q.peek()); // 獲取總長(zhǎng)度 System.out.println(q.size()); // 遍歷 for (String s : q) { System.out.println(s); } }
結(jié)果:
Java
C#
3
C#
Javascript
Python
阻塞隊(duì)列,顧名思義,首先它是一個(gè)隊(duì)列,通過(guò)一個(gè)共享的隊(duì)列,可以使得數(shù)據(jù)由隊(duì)列的一端輸入,從另外一端輸出;
常用的隊(duì)列主要有以下兩種:(當(dāng)然通過(guò)不同的實(shí)現(xiàn)方式,還可以延伸出很多不同類型的隊(duì)列,DelayQueue就是其中的一種)
先進(jìn)先出(FIFO):先插入的隊(duì)列的元素也最先出隊(duì)列,類似于排隊(duì)的功能。從某種程度上來(lái)說(shuō)這種隊(duì)列也體現(xiàn)了一種公平性。
后進(jìn)先出(LIFO):后插入隊(duì)列的元素最先出隊(duì)列,這種隊(duì)列優(yōu)先處理最近發(fā)生的事件。
多線程環(huán)境中,通過(guò)隊(duì)列可以很容易實(shí)現(xiàn)數(shù)據(jù)共享,比如經(jīng)典的“生產(chǎn)者”和“消費(fèi)者”模型中,通過(guò)隊(duì)列可以很便利地實(shí)現(xiàn)兩者之間的數(shù)據(jù)共享。假設(shè)我們有若干生產(chǎn)者線程,另外又有若干個(gè)消費(fèi)者線程。如果生產(chǎn)者線程需要把準(zhǔn)備好的數(shù)據(jù)共享給消費(fèi)者線程,利用隊(duì)列的方式來(lái)傳遞數(shù)據(jù),就可以很方便地解決他們之間的數(shù)據(jù)共享問(wèn)題。但如果生產(chǎn)者和消費(fèi)者在某個(gè)時(shí)間段內(nèi),萬(wàn)一發(fā)生數(shù)據(jù)處理速度不匹配的情況呢?理想情況下,如果生產(chǎn)者產(chǎn)出數(shù)據(jù)的速度大于消費(fèi)者消費(fèi)的速度,并且當(dāng)生產(chǎn)出來(lái)的數(shù)據(jù)累積到一定程度的時(shí)候,那么生產(chǎn)者必須暫停等待一下(阻塞生產(chǎn)者線程),以便等待消費(fèi)者線程把累積的數(shù)據(jù)處理完畢,反之亦然。然而,在concurrent包發(fā)布以前,在多線程環(huán)境下,我們每個(gè)程序員都必須去自己控制這些細(xì)節(jié),尤其還要兼顧效率和線程安全,而這會(huì)給我們的程序帶來(lái)不小的復(fù)雜度。好在此時(shí),強(qiáng)大的concurrent包橫空出世了,而他也給我們帶來(lái)了強(qiáng)大的BlockingQueue。(在多線程領(lǐng)域:所謂阻塞,在某些情況下會(huì)掛起線程(即阻塞),一旦條件滿足,被掛起的線程又會(huì)自動(dòng)被喚醒)
阻塞隊(duì)列(BlockingQueue)是一個(gè)支持兩個(gè)附加操作的隊(duì)列。這兩個(gè)附加的操作是:
在隊(duì)列為空時(shí),獲取元素的線程會(huì)等待隊(duì)列變?yōu)榉强铡?br>當(dāng)隊(duì)列滿時(shí),存儲(chǔ)元素的線程會(huì)等待隊(duì)列可用。
阻塞隊(duì)列常用于生產(chǎn)者和消費(fèi)者的場(chǎng)景,生產(chǎn)者是往隊(duì)列里添加元素的線程,消費(fèi)者是從隊(duì)列里拿元素的線程。阻塞隊(duì)列就是生產(chǎn)者存放元素的容器,而消費(fèi)者也只從容器里拿元素。
BlockingQueue即阻塞隊(duì)列,從阻塞這個(gè)詞可以看出,在某些情況下對(duì)阻塞隊(duì)列的訪問(wèn)可能會(huì)造成阻塞。被阻塞的情況主要有如下兩種:
當(dāng)隊(duì)列滿了的時(shí)候進(jìn)行入隊(duì)列操作
當(dāng)隊(duì)列空了的時(shí)候進(jìn)行出隊(duì)列操作
因此,當(dāng)一個(gè)線程試圖對(duì)一個(gè)已經(jīng)滿了的隊(duì)列進(jìn)行入隊(duì)列操作時(shí),它將會(huì)被阻塞,除非有另一個(gè)線程做了出隊(duì)列操作;同樣,當(dāng)一個(gè)線程試圖對(duì)一個(gè)空隊(duì)列進(jìn)行出隊(duì)列操作時(shí),它將會(huì)被阻塞,除非有另一個(gè)線程進(jìn)行了入隊(duì)列操作。
在Java中,BlockingQueue的接口位于java.util.concurrent 包中(在Java5版本開(kāi)始提供),由上面介紹的阻塞隊(duì)列的特性可知,阻塞隊(duì)列是線程安全的。
在新增的Concurrent包中,BlockingQueue很好的解決了多線程中,如何高效安全“傳輸”數(shù)據(jù)的問(wèn)題。通過(guò)這些高效并且線程安全的隊(duì)列類,為我們快速搭建高質(zhì)量的多線程程序帶來(lái)極大的便利。
下表顯示了jdk1.5中的阻塞隊(duì)列的操作:
add 增加一個(gè)元索 如果隊(duì)列已滿,則拋出一個(gè)IIIegaISlabEepeplian異常
remove 移除并返回隊(duì)列頭部的元素 如果隊(duì)列為空,則拋出一個(gè)NoSuchElementException異常
element 返回隊(duì)列頭部的元素 如果隊(duì)列為空,則拋出一個(gè)NoSuchElementException異常
offer 添加一個(gè)元素并返回true 如果隊(duì)列已滿,則返回false
poll 移除并返問(wèn)隊(duì)列頭部的元素 如果隊(duì)列為空,則返回null
peek 返回隊(duì)列頭部的元素 如果隊(duì)列為空,則返回null
put 添加一個(gè)元素 如果隊(duì)列滿,則阻塞
take 移除并返回隊(duì)列頭部的元素 如果隊(duì)列為空,則阻塞
阻塞隊(duì)列操作:
aad、remove和element操作在你試圖為一個(gè)已滿的隊(duì)列增加元素或從空隊(duì)列取得元素時(shí) 拋出異常
offer、poll、peek方法。這些方法在無(wú)法完成任務(wù)時(shí),只是給出一個(gè)出錯(cuò)提示而不會(huì)拋出異常
阻塞操作put和take。put方法在隊(duì)列滿時(shí)阻塞,take方法在隊(duì)列空時(shí)阻塞。
ArrayBlockingQueue是一個(gè)有邊界的阻塞隊(duì)列,它的內(nèi)部實(shí)現(xiàn)是一個(gè)數(shù)組。有邊界的意思是它的容量是有限的,我們必須在其初始化的時(shí)候指定它的容量大小,容量大小一旦指定就不可改變。
ArrayBlockingQueue是以先進(jìn)先出的方式存儲(chǔ)數(shù)據(jù),最新插入的對(duì)象是尾部,最新移出的對(duì)象是頭部
LinkedBlockingQueue阻塞隊(duì)列大小的配置是可選的,如果我們初始化時(shí)指定一個(gè)大小,它就是有邊界的,如果不指定,它就是無(wú)邊界的。說(shuō)是無(wú)邊界,其實(shí)是采用了默認(rèn)大小為Integer.MAX_VALUE的容量 。它的內(nèi)部實(shí)現(xiàn)是一個(gè)鏈表。
和ArrayBlockingQueue一樣,LinkedBlockingQueue 也是以先進(jìn)先出的方式存儲(chǔ)數(shù)據(jù),最新插入的對(duì)象是尾部,最新移出的對(duì)象是頭部。
PriorityBlockingQueue是一個(gè)沒(méi)有邊界的隊(duì)列,它的排序規(guī)則和 java.util.PriorityQueue一樣。需要注意,PriorityBlockingQueue中允許插入null對(duì)象。
所有插入PriorityBlockingQueue的對(duì)象必須實(shí)現(xiàn) java.lang.Comparable接口,隊(duì)列優(yōu)先級(jí)的排序規(guī)則就是按照我們對(duì)這個(gè)接口的實(shí)現(xiàn)來(lái)定義的。
另外,我們可以從PriorityBlockingQueue獲得一個(gè)迭代器Iterator,但這個(gè)迭代器并不保證按照優(yōu)先級(jí)順序進(jìn)行迭代。
SynchronousQueue隊(duì)列內(nèi)部?jī)H允許容納一個(gè)元素。當(dāng)一個(gè)線程插入一個(gè)元素后會(huì)被阻塞,除非這個(gè)元素被另一個(gè)線程消費(fèi)
DelayQueue(基于PriorityQueue來(lái)實(shí)現(xiàn)的)是一個(gè)存放Delayed 元素的無(wú)界阻塞隊(duì)列,只有在延遲期滿時(shí)才能從中提取元素。該隊(duì)列的頭部是延遲期滿后保存時(shí)間最長(zhǎng)的 Delayed 元素。如果延遲都還沒(méi)有期滿,則隊(duì)列沒(méi)有頭部,并且poll將返回null。當(dāng)一個(gè)元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一個(gè)小于或等于零的值時(shí),則出現(xiàn)期滿,poll就以移除這個(gè)元素了。此隊(duì)列不允許使用 null 元素。
使用BlockingQueue模擬生產(chǎn)者與消費(fèi)者生產(chǎn)者
public class ProducerThread implements Runnable { private BlockingQueuequeue; private AtomicInteger count = new AtomicInteger(); private volatile boolean FLAG = true; public ProducerThread(BlockingQueue queue) { this.queue = queue; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "生產(chǎn)者開(kāi)始啟動(dòng)...."); while (FLAG) { String data = count.incrementAndGet() + ""; try { boolean offer = queue.offer(data, 2, TimeUnit.SECONDS); if (offer) { System.out.println(Thread.currentThread().getName() + ",生產(chǎn)隊(duì)列" + data + "成功.."); } else { System.out.println(Thread.currentThread().getName() + ",生產(chǎn)隊(duì)列" + data + "失敗.."); } Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + ",生產(chǎn)者線程停止..."); } public void stop() { this.FLAG = false; } }
消費(fèi)者
public class ConsumerThread implements Runnable { private volatile boolean FLAG = true; private BlockingQueueblockingQueue; public ConsumerThread(BlockingQueue blockingQueue) { this.blockingQueue = blockingQueue; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "消費(fèi)者開(kāi)始啟動(dòng)...."); while (FLAG) { try { String data = blockingQueue.poll(2, TimeUnit.SECONDS); if (data == null || data == "") { FLAG = false; System.out.println("消費(fèi)者超過(guò)2秒時(shí)間未獲取到消息."); return; } System.out.println("消費(fèi)者獲取到隊(duì)列信息成功,data:" + data); } catch (Exception e) { e.printStackTrace(); } } } }
請(qǐng)求
@RequestMapping("test-blockingQueue") public void testBlockingQueue() { LinkedBlockingDequeblockingDeque = new LinkedBlockingDeque<>(1); ProducerThread producerThread = new ProducerThread(blockingDeque); ConsumerThread consumerThread = new ConsumerThread(blockingDeque); Thread t1 = new Thread(producerThread, "生產(chǎn)者"); Thread t2 = new Thread(consumerThread, "消費(fèi)者"); t1.start(); t2.start(); // 10秒后停止線程 try { Thread.sleep(10 * 1000); producerThread.stop(); } catch (InterruptedException e) { e.printStackTrace(); } }
結(jié)果
消費(fèi)者消費(fèi)者開(kāi)始啟動(dòng)....
生產(chǎn)者生產(chǎn)者開(kāi)始啟動(dòng)....
生產(chǎn)者,生產(chǎn)隊(duì)列1成功..
消費(fèi)者獲取到隊(duì)列信息成功,data:1
生產(chǎn)者,生產(chǎn)隊(duì)列2成功..
消費(fèi)者獲取到隊(duì)列信息成功,data:2
生產(chǎn)者,生產(chǎn)隊(duì)列3成功..
消費(fèi)者獲取到隊(duì)列信息成功,data:3
生產(chǎn)者,生產(chǎn)隊(duì)列4成功..
消費(fèi)者獲取到隊(duì)列信息成功,data:4
生產(chǎn)者,生產(chǎn)隊(duì)列5成功..
消費(fèi)者獲取到隊(duì)列信息成功,data:5
生產(chǎn)者,生產(chǎn)隊(duì)列6成功..
消費(fèi)者獲取到隊(duì)列信息成功,data:6
生產(chǎn)者,生產(chǎn)隊(duì)列7成功..
消費(fèi)者獲取到隊(duì)列信息成功,data:7
生產(chǎn)者,生產(chǎn)隊(duì)列8成功..
消費(fèi)者獲取到隊(duì)列信息成功,data:8
生產(chǎn)者,生產(chǎn)隊(duì)列9成功..
消費(fèi)者獲取到隊(duì)列信息成功,data:9
生產(chǎn)者,生產(chǎn)隊(duì)列10成功..
消費(fèi)者獲取到隊(duì)列信息成功,data:10
生產(chǎn)者,生產(chǎn)者線程停止...
消費(fèi)者超過(guò)2秒時(shí)間未獲取到消息.
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/74729.html
摘要:知識(shí)點(diǎn)總結(jié)容器知識(shí)點(diǎn)總結(jié)容器接口與是在同一級(jí)別,都是繼承了接口。另一種隊(duì)列則是雙端隊(duì)列,支持在頭尾兩端插入和移除元素,主要包括。一個(gè)由鏈表結(jié)構(gòu)組成的無(wú)界阻塞隊(duì)列。是一個(gè)阻塞的線程安全的隊(duì)列,底層實(shí)現(xiàn)也是使用鏈?zhǔn)浇Y(jié)構(gòu)。 Java知識(shí)點(diǎn)總結(jié)(Java容器-Queue) @(Java知識(shí)點(diǎn)總結(jié))[Java, Java容器] Queue Queue接口與List、Set是在同一級(jí)別,都是繼承了...
摘要:語(yǔ)言在之前,提供的唯一的并發(fā)原語(yǔ)就是管程,而且之后提供的并發(fā)包,也是以管程技術(shù)為基礎(chǔ)的。但是管程更容易使用,所以選擇了管程。線程進(jìn)入條件變量的等待隊(duì)列后,是允許其他線程進(jìn)入管程的。并發(fā)編程里兩大核心問(wèn)題互斥和同步,都可以由管程來(lái)幫你解決。 并發(fā)編程這個(gè)技術(shù)領(lǐng)域已經(jīng)發(fā)展了半個(gè)世紀(jì)了。有沒(méi)有一種核心技術(shù)可以很方便地解決我們的并發(fā)問(wèn)題呢?這個(gè)問(wèn)題, 我會(huì)選擇 Monitor(管程)技術(shù)。Ja...
摘要:什么是阻塞隊(duì)列阻塞隊(duì)列是一個(gè)在隊(duì)列基礎(chǔ)上又支持了兩個(gè)附加操作的隊(duì)列。阻塞隊(duì)列的應(yīng)用場(chǎng)景阻塞隊(duì)列常用于生產(chǎn)者和消費(fèi)者的場(chǎng)景,生產(chǎn)者是向隊(duì)列里添加元素的線程,消費(fèi)者是從隊(duì)列里取元素的線程。由鏈表結(jié)構(gòu)組成的無(wú)界阻塞隊(duì)列。 什么是阻塞隊(duì)列? 阻塞隊(duì)列是一個(gè)在隊(duì)列基礎(chǔ)上又支持了兩個(gè)附加操作的隊(duì)列。 2個(gè)附加操作: 支持阻塞的插入方法:隊(duì)列滿時(shí),隊(duì)列會(huì)阻塞插入元素的線程,直到隊(duì)列不滿。 支持阻塞的...
摘要:隊(duì)列中有元素時(shí),就說(shuō)明有過(guò)期了,線程繼續(xù)執(zhí)行,然后元素出隊(duì),根據(jù)相應(yīng)的移除緩存。所以嚴(yán)格來(lái)說(shuō),雖然實(shí)現(xiàn)了隊(duì)列接口,但是它的目的卻并不是隊(duì)列,而是將生產(chǎn)者消費(fèi)者線程配對(duì)。轉(zhuǎn)移隊(duì)列鏈?zhǔn)睫D(zhuǎn)移隊(duì)列。 引言 本周在編寫短信驗(yàn)證碼頻率限制切面的時(shí)候,經(jīng)潘老師給的實(shí)現(xiàn)思路,使用隊(duì)列進(jìn)行實(shí)現(xiàn)。 看了看java.util包下的Queue接口,發(fā)現(xiàn)還從來(lái)沒(méi)用過(guò)呢! Collection集合類接口,由它派生...
閱讀 1464·2021-11-24 09:39
閱讀 1783·2021-11-22 15:25
閱讀 3736·2021-11-19 09:40
閱讀 3296·2021-09-22 15:31
閱讀 1296·2021-07-29 13:49
閱讀 1205·2019-08-26 11:59
閱讀 1318·2019-08-26 11:39
閱讀 928·2019-08-26 11:00