詳解Java中的延時(shí)隊(duì)列 DelayQueue
當(dāng)用戶超時(shí)未支付時(shí),給用戶發(fā)提醒消息。另一種場(chǎng)景是,超時(shí)未付款,訂單自動(dòng)取消。通常,訂單創(chuàng)建的時(shí)候可以向延遲隊(duì)列種插入一條消息,到時(shí)間自動(dòng)執(zhí)行。其實(shí),也可以用臨時(shí)表,把這些未支付的訂單放到一個(gè)臨時(shí)表中,或者Redis,然后定時(shí)任務(wù)去掃描。這里我們用延時(shí)隊(duì)列來(lái)做。RocketMQ有延時(shí)隊(duì)列,RibbitMQ也可以實(shí)現(xiàn),Java自帶的也有延時(shí)隊(duì)列,接下來(lái)就回顧一下各種隊(duì)列。
Queue
隊(duì)列是一種集合。除了基本的集合操作以外,隊(duì)列還提供了額外的插入、提取和檢查操作。隊(duì)列的每個(gè)方法都以兩種形式存在:一種是當(dāng)操作失敗時(shí)拋異常,另一種是返回一個(gè)特定的值(null或者false,取決于具體操作)。后一種形式的插入操作是專門設(shè)計(jì)用于有界隊(duì)列實(shí)現(xiàn)的,在大多情況下,插入操作不會(huì)失敗。
隊(duì)列通常(但不一定)以FIFO(先進(jìn)先出)的方式對(duì)元素進(jìn)行排序。例外情況包括優(yōu)先級(jí)隊(duì)列(根據(jù)提供的比較器對(duì)元素進(jìn)行排序或元素的自然排序)和LIFO隊(duì)列(或堆棧),對(duì)LIFO進(jìn)行排序(后進(jìn)先出)。無(wú)論使用哪種順序,隊(duì)列的開(kāi)頭都是該元素,可以通過(guò)調(diào)用remove()或poll()將其刪除。在FIFO隊(duì)列中,所有新元素都插入隊(duì)列的尾部。其他種類的隊(duì)列可能使用不同的放置規(guī)則。 每個(gè)Queue實(shí)現(xiàn)必須指定其排序?qū)傩?。無(wú)論使用哪種順序,都可以通過(guò)調(diào)用remove()或poll()來(lái)刪除隊(duì)列開(kāi)頭的元素。在FIFO隊(duì)列中,所有新元素都插入到隊(duì)列的尾部。其他類型的隊(duì)列可能使用不同的放置規(guī)則。每個(gè)隊(duì)列實(shí)現(xiàn)都必須指定其排序?qū)傩浴?/p>
offer方法在可以的情況下會(huì)向隊(duì)列種插入一個(gè)元素,否則返回false。這不同于Collection.add方法,后者只能通過(guò)拋異常來(lái)添加元素。offer方法設(shè)計(jì)用于在正常情況下(而不是在例外情況下)發(fā)生故障時(shí),例如在固定容量(或者“有界”)隊(duì)列種使用。
remove()和poll()方法刪除并返回隊(duì)頭元素。當(dāng)隊(duì)列為空時(shí),remove()拋出異常,而poll()返回null。
element()和peek()方法返回隊(duì)頭元素。
PriorityQueue
PriorityQueue是一個(gè)無(wú)界優(yōu)先級(jí)隊(duì)列是基于優(yōu)先級(jí)堆的。優(yōu)先級(jí)隊(duì)列種的元素根據(jù)自然順序進(jìn)行排序,或者通過(guò)在隊(duì)列構(gòu)建時(shí)提供的Comparator進(jìn)行排序,當(dāng)然這取決于使用哪種構(gòu)造函數(shù)。優(yōu)先級(jí)隊(duì)列不允許空(null)元素。一個(gè)依賴自然順序的優(yōu)先級(jí)隊(duì)列也不允許插入不可比較的對(duì)象。
優(yōu)先級(jí)隊(duì)列的隊(duì)頭元素是最小的元素,如果有多個(gè)元素并列最小,那么隊(duì)頭是它們其中之一。
優(yōu)先級(jí)隊(duì)列是無(wú)界的,但是有一個(gè)內(nèi)部容量來(lái)控制用于在隊(duì)列上存儲(chǔ)元素的數(shù)組的大小。它總是至少與隊(duì)列大小一樣大。將元素添加到優(yōu)先級(jí)隊(duì)列時(shí),其容量會(huì)自動(dòng)增長(zhǎng)
BlockingQueue
這種隊(duì)列還支持以下操作:在檢索元素時(shí)等待隊(duì)列變?yōu)榉强?,并在存?chǔ)元素時(shí)等待隊(duì)列中的空間變?yōu)榭捎谩?/p>
BlockingQueue方法有四種形式,它們以不同的方式處理操作,這些操作無(wú)法立即滿足,但將來(lái)可能會(huì)滿足:一種拋出異常,第二種返回特殊值(null或false,取決于具體操作),第三種阻塞當(dāng)前線程,直到操作成功為止;第四種阻塞當(dāng)前線程,超時(shí)則放棄。 下表總結(jié)了這些方法:
阻塞隊(duì)列不接受空元素,如果你試圖add , put 或者 offer 一個(gè)null,將會(huì)拋NullPointerException。
阻塞隊(duì)列是線程安全的。所有排隊(duì)方法都使用內(nèi)部鎖或者其他形式的并發(fā)控制來(lái)保證以原子方式實(shí)現(xiàn)它們的效果。
阻塞隊(duì)列被設(shè)計(jì)主要用于生產(chǎn)者-消費(fèi)者隊(duì)列。
下面是一個(gè)典型的生產(chǎn)者-消費(fèi)者方案:
package com.example; import java.text.MessageFormat; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; /** * @author ChengJianSheng * @date 2020/12/15 */ public class Setup { public static void main(String[] args) { BlockingQueue<Bread> queue = new ArrayBlockingQueue<>(5); Producer p1 = new Producer(queue); Producer p2 = new Producer(queue); Consumer c1 = new Consumer(queue); Consumer c2 = new Consumer(queue); new Thread(p1, "p1").start(); new Thread(p2, "p2").start(); new Thread(c1, "c1").start(); new Thread(c2, "c2").start(); } } class Bread { } /** * 生產(chǎn)者 */ class Producer implements Runnable { private final BlockingQueue<Bread> queue; public Producer(BlockingQueue<Bread> queue) { this.queue = queue; } @Override public void run() { try { while (true) { queue.put(produce()); } } catch (InterruptedException e) { e.printStackTrace(); } } public Bread produce() { try { Thread.sleep(Math.round(2000)); } catch (InterruptedException e) { e.printStackTrace(); } return new Bread(); } } /** * 消費(fèi)者 */ class Consumer implements Runnable { private final BlockingQueue<Bread> queue; public Consumer(BlockingQueue<Bread> queue) { this.queue = queue; } @Override public void run() { try { while (true) { consume(queue.take()); } } catch (InterruptedException e) { e.printStackTrace(); } } public void consume(Bread bread) { try { Thread.sleep(Math.round(2000)); } catch (InterruptedException e) { e.printStackTrace(); } } }
ArrayBlockingQueue
ArrayBlockingQueue是用數(shù)組實(shí)現(xiàn)的有界阻塞隊(duì)列。這種隊(duì)列中的元素按FIFO(先進(jìn)先出)排序。隊(duì)頭是在隊(duì)列中停留最長(zhǎng)時(shí)間的元素。隊(duì)尾是在隊(duì)列中停留時(shí)間最短的元素。新元素插入到隊(duì)列的尾部,并且隊(duì)列檢索操作在隊(duì)列的頭部獲取元素。
這是一個(gè)經(jīng)典的“有界緩沖區(qū)”,其中固定大小的數(shù)組包含由生產(chǎn)者插入并由消費(fèi)者提取的元素。 創(chuàng)建后,容量將無(wú)法更改。 試圖將一個(gè)元素放入一個(gè)已滿的隊(duì)列將導(dǎo)致操作阻塞; 試圖從空隊(duì)列中取出一個(gè)元素也會(huì)阻塞。
這個(gè)類支持一個(gè)可選的公平性策略,用于對(duì)等待的生產(chǎn)者和消費(fèi)者線程進(jìn)行排序。默認(rèn)情況下,不保證這個(gè)順序。然而,將公平性設(shè)置為true的隊(duì)列將按FIFO順序授予線程訪問(wèn)權(quán)。公平性通常會(huì)降低吞吐量,但會(huì)降低可變性并避免饑餓。
LinkedBlockingQueue
LinkedBlockingQueue是一個(gè)基于鏈表實(shí)現(xiàn)的可選邊界的阻塞隊(duì)列。
PriorityBlockingQueue
PriorityBlockingQueue是一個(gè)無(wú)界阻塞隊(duì)列,它使用與PriorityQueue相同的排序規(guī)則,并提供阻塞檢索操作。
DelayQueue
DelayQueue是一種由延遲元素組成的無(wú)界阻塞隊(duì)列,在該隊(duì)列中,僅當(dāng)元素的延遲到期時(shí)才可以使用該元素。隊(duì)頭是已經(jīng)過(guò)期的延遲元素,它已過(guò)期時(shí)間最長(zhǎng)。如果沒(méi)有過(guò)期的延遲,則隊(duì)列沒(méi)有頭部,此時(shí)調(diào)用poll將返回null。當(dāng)調(diào)用元素的getDelay(TimeUnit.NANOSECONDS)方法返回值小于或等于0時(shí),就會(huì)發(fā)生過(guò)期。即使元素沒(méi)有過(guò)期,也不能用take或者poll將其刪除。
AbstractQueuedSynchronizer
AbstractQueuedSynchronizer提供了一個(gè)框架來(lái)實(shí)現(xiàn)依賴于先進(jìn)先出(FIFO)等待隊(duì)列的阻塞鎖和相關(guān)的同步器(信號(hào)燈,事件等)。該類旨在為大多數(shù)依賴單個(gè)原子int值表示狀態(tài)的同步器提供有用的基礎(chǔ)。子類必須定義更改此狀態(tài)的受保護(hù)方法,并定義該狀態(tài)對(duì)于獲取或釋放此對(duì)象而言意味著什么。 鑒于這些,此類中的其他方法將執(zhí)行所有排隊(duì)和阻塞機(jī)制。 子類可以維護(hù)其他狀態(tài)字段,但是僅跟蹤關(guān)于同步的使用方法getState(),setState(int)和compareAndSetState(int,int)操作的原子更新的int值。
小結(jié)
1、Queue是一個(gè)集合,隊(duì)列的每個(gè)方法都有兩種形式,一種是拋異常,另一種是返回一個(gè)特定的值。
2、PriorityQueue是一個(gè)無(wú)界優(yōu)先級(jí)隊(duì)列,默認(rèn)情況下,隊(duì)列種的元素按自然順序排序,或者根據(jù)提供的Comparator進(jìn)行排序。也就是說(shuō),優(yōu)先級(jí)隊(duì)列種的元素都是經(jīng)過(guò)排序的,排序規(guī)則可以自己指定,同時(shí)隊(duì)列種的元素都必須是可排序的。
3、BlockingQueue是一個(gè)阻塞隊(duì)列,向已滿的隊(duì)列種插入元素時(shí)會(huì)阻塞,向空隊(duì)列中取元素時(shí)也會(huì)阻塞;阻塞隊(duì)列被設(shè)計(jì)主要用于生產(chǎn)者-消費(fèi)者隊(duì)列。
4、ArrayBlockingQueue是用數(shù)組實(shí)現(xiàn)的有界阻塞隊(duì)列,隊(duì)列種的元素按FIFO(先進(jìn)先出)排序。
5、LinkedBlockingQueue是用鏈表實(shí)現(xiàn)的可選邊界的阻塞隊(duì)列。
6、PriorityBlockingQueue相當(dāng)于是阻塞隊(duì)列和優(yōu)先級(jí)隊(duì)列的合體,排序規(guī)則與優(yōu)先級(jí)隊(duì)列相同。
7、DelayQueue延時(shí)隊(duì)列中的元素都有一個(gè)有效期,只有當(dāng)過(guò)了有效期才可以使用該元素。
以上就是詳解Java中的延時(shí)隊(duì)列 DelayQueue的詳細(xì)內(nèi)容,更多關(guān)于Java 延時(shí)隊(duì)列 DelayQueue的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
spring task @Scheduled注解各參數(shù)的用法
這篇文章主要介紹了spring task @Scheduled注解各參數(shù)的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Java使用貪心算法解決電臺(tái)覆蓋問(wèn)題(示例詳解)
貪心算法是指在對(duì)問(wèn)題進(jìn)行求解時(shí),在每一步選擇中都采取最好或最優(yōu)的選擇,從而導(dǎo)致結(jié)果理想化,下面通過(guò)本文介紹下Java使用貪心算法解決電臺(tái)覆蓋問(wèn)題,需要的朋友可以參考下2022-04-04JAVASE精密邏輯控制過(guò)程詳解(分支和循環(huán)語(yǔ)句)
在一個(gè)程序執(zhí)行的過(guò)程中各條語(yǔ)句的執(zhí)行順序?qū)Τ绦虻慕Y(jié)果是有直接影響的,這篇文章主要給大家介紹了關(guān)于JAVASE精密邏輯控制(分支和循環(huán)語(yǔ)句)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-04-04Spring 動(dòng)態(tài)代理實(shí)現(xiàn)代碼實(shí)例
這篇文章主要介紹了Spring 動(dòng)態(tài)代理實(shí)現(xiàn)代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09idea編譯報(bào)錯(cuò)-代碼沒(méi)問(wèn)題IDEA編譯不通過(guò)的處理方案
這篇文章主要介紹了idea編譯報(bào)錯(cuò)-代碼沒(méi)問(wèn)題IDEA編譯不通過(guò)的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12