Java?延時(shí)隊(duì)列及簡(jiǎn)單使用方式詳解
Java 延時(shí)隊(duì)列,簡(jiǎn)單使用方式
前言:首先我們要知道延時(shí)隊(duì)列是什么?可以用來干什么?
是什么?
看名稱
隊(duì)列
可想而知先進(jìn)先出
的一個(gè)集合。但我們的延時(shí)隊(duì)列并沒有完全的遵循這個(gè)理念。
DelayQueue
內(nèi)部其實(shí)是基于我們的優(yōu)先隊(duì)列
來實(shí)現(xiàn)的,也就是元素的先后順序是按元素的Comparable
接口提供的順序來出隊(duì)
的。
DelayQueue
內(nèi)部的元素必須是Delayed
的的實(shí)現(xiàn)類 而Delayed
的父接口是Comparable<Delayed>
。
Delayed
接口中需要我們實(shí)現(xiàn)一個(gè)long getDelay(TimeUnit unit);
元素的剩余時(shí)間TTL
。
干什么?
- 定時(shí)任務(wù)。
- 重試(5秒重試)。
- 延時(shí)通知。
都可以基于延時(shí)隊(duì)列來實(shí)現(xiàn)。
開發(fā)中遇到一個(gè)這樣的需求。
需要一個(gè)集合來維護(hù)熱點(diǎn)數(shù)據(jù),但這個(gè)熱點(diǎn)數(shù)據(jù)是有時(shí)效性的,我們?cè)谌ゲ樵兊臅r(shí)候先要判斷下這個(gè)熱點(diǎn)數(shù)據(jù)不存在,或者不在有效期內(nèi)的。才能走剩下的邏輯。
當(dāng)然這個(gè)需求聽起來并不復(fù)雜。
我們自己實(shí)現(xiàn)的話。
是不是需要一個(gè)有順序的集合?
是不是還每個(gè)元素都有一個(gè)有效期?
是不是還得保證元素出隊(duì)的順序?
如果說還有另外一個(gè)地方需要復(fù)用,我們是不是還得再寫一套這樣的邏輯。
所以,我們可以直接使用
DelayQueue
來實(shí)現(xiàn)這個(gè)需求,他天生就能保證我們的集合是有順序的,并且保證過期的元素不在集合內(nèi)。
簡(jiǎn)單使用
延時(shí)對(duì)象
@Data static class SimpleDelayed<T> implements Delayed { /** * 對(duì)象創(chuàng)建時(shí)間 */ @JsonIgnore private final Date createDate; /** * 延時(shí)數(shù)(單位毫秒) */ private long delayCount; private T data; public SimpleDelayed(T data, long delayCount) { this.data = data; this.delayCount = delayCount; this.createDate = new Date(); } @Override public long getDelay(TimeUnit unit) { long diff = this.createDate.getTime() + this.delayCount - System.currentTimeMillis(); return unit.convert(diff, TimeUnit.MILLISECONDS); } @Override public int compareTo(Delayed delayed) { return Long.compare(this.getDelay(TimeUnit.MILLISECONDS),delayed.getDelay(TimeUnit.MILLISECONDS)); } }
注意: compareTo
提供的順序必須要與 getDelay
否則會(huì)出現(xiàn)元素過期了但是在隊(duì)列中。因?yàn)?DelayQueue
內(nèi)部維護(hù)的是一個(gè) 優(yōu)先隊(duì)列
。所以必須保證這兩順序是一致的。所以這里的 compareTo
直接比較了這兩的 剩余過期數(shù)
越小的越先出隊(duì)。反之會(huì)出現(xiàn), 延時(shí)時(shí)間
已過,但是沒有出隊(duì)。 compareTo
靠前,但是過期數(shù)未過的數(shù)卡著。
延時(shí)隊(duì)列工具類
@Slf4j public class SimpleDelayQueueUtils { /** * 1.定義了一個(gè)延時(shí)隊(duì)列 隊(duì)列中元素是我們的SimpleDelayed */ private final static DelayQueue<SimpleDelayed> SIMPLE_DELAYED_DELAY_QUEUE = new DelayQueue<>(); static{ // 2.聲明一個(gè)異步線程 CompletableFuture.runAsync(() -> { // 3.通過自旋去出隊(duì)已過期的元素 for (;;) { try { // 4.take() 對(duì)頭元素出隊(duì)。 log.info("元素已過期{}",SIMPLE_DELAYED_DELAY_QUEUE.take().getData()); } catch (InterruptedException e) { throw new RuntimeException(e); } } // 單個(gè)線程的線程池,為了不占用fork-join線程池 }, Executors.newSingleThreadExecutor()); } public static void add(SimpleDelayed... delayeds) { SIMPLE_DELAYED_DELAY_QUEUE.addAll(Arrays.asList(delayeds)); } // 測(cè)試 public static void main(String[] args) { SimpleDelayed<String> nonceDelayed = new SimpleDelayQueueUtils.SimpleDelayed<>("111",5001); SimpleDelayed<String> nonceDelayed1 = new SimpleDelayQueueUtils.SimpleDelayed<>("222", 4500); SimpleDelayQueueUtils.add(nonceDelayed,nonceDelayed1); } }
輸出
23:35:00.693 [pool-1-thread-1] INFO com.mfyuan.chenapioperation.util.SimpleDelayQueueUtils - 元素已過期222
23:35:01.189 [pool-1-thread-1] INFO com.mfyuan.chenapioperation.util.SimpleDelayQueueUtils - 元素已過期111
思考
這里的 data
元素是可以靈活替換的,因?yàn)槲疫@里的需求是涉及到隊(duì)列中的元素是否過期。所以這些就已經(jīng)足夠了。
但是當(dāng)我們把 data
,替換成 Runable
再把出隊(duì)的的元素通過線程池的方式去調(diào)用則就實(shí)現(xiàn)了 定時(shí)任務(wù)
。 重試
也可以當(dāng)做 定時(shí)任務(wù)
來理解,就把當(dāng)單做一個(gè)任務(wù),如果任務(wù)執(zhí)行失敗了或者出現(xiàn)異常了,那么我們重新講元素放入延時(shí)隊(duì)列中即可。
后話
Redis來實(shí)現(xiàn)我這個(gè)功能,雖然可以實(shí)現(xiàn)但是有些地方實(shí)現(xiàn)的邏輯也是不簡(jiǎn)單。更別說引入 redis
的開發(fā)成本,殺??不需要??刀。
Redis zset
是可以保證集合的元素有順序的, zset
的 ttl
是指這個(gè) zset
整體的過期時(shí)間。(不確定,)
到此這篇關(guān)于Java 延時(shí)隊(duì)列,簡(jiǎn)單使用方式的文章就介紹到這了,更多相關(guān)java延時(shí)隊(duì)列內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript中HTML元素操作的實(shí)現(xiàn)
本文主要介紹了JavaScript中HTML元素操作的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06Java 異步回調(diào)機(jī)制實(shí)例分析
這篇文章主要介紹了Java 異步回調(diào)機(jī)制實(shí)例解析的相關(guān)資料,需要的朋友可以參考下2017-02-02SpringBoot中使用Redisson的實(shí)現(xiàn)示例
Redission是一個(gè)強(qiáng)大的Java庫,用于構(gòu)建和管理分布式系統(tǒng)中的緩存和任務(wù)調(diào)度,本文主要介紹了SpringBoot中使用Redisson的實(shí)現(xiàn)示例,感興趣的可以了解一下2023-12-12java list中包含某個(gè)字符串的兩種方法實(shí)現(xiàn)
在Java開發(fā)中,經(jīng)常需要判斷一個(gè)List中是否包含特定的字符串,包括使用contains()方法和循環(huán)遍歷判斷,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03JSP頁面pageEncoding和contentType屬性
有關(guān)于JSP頁面中pageEncoding和contentType屬性。2013-04-04java線程池:獲取運(yùn)行線程數(shù)并控制線程啟動(dòng)速度的方法
下面小編就為大家?guī)硪黄猨ava線程池:獲取運(yùn)行線程數(shù)并控制線程啟動(dòng)速度的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05Spring?Security內(nèi)置過濾器的維護(hù)方法
這篇文章主要介紹了Spring?Security的內(nèi)置過濾器是如何維護(hù)的,本文給我們分析一下HttpSecurity維護(hù)過濾器的幾個(gè)方法,需要的朋友可以參考下2022-02-02