java通過DelayQueue實現(xiàn)延時任務(wù)
實現(xiàn)延時任務(wù)有很多的方法,網(wǎng)上關(guān)于延時任務(wù)的實現(xiàn)的文章已經(jīng)不少了。比如:實現(xiàn)延時任務(wù)的10種方法等等。但是這些文章基本上都是將方法大概的列舉一下,給出部分示例代碼,對于有經(jīng)驗的老程序員可能一看就知道該怎么去把它實現(xiàn)完整,但是對于初學(xué)者來說不夠友好。
小概念:什么是延時任務(wù)?舉個例子:你買了一張火車票,必須在30分鐘之內(nèi)付款,否則該訂單被自動取消?!赣唵?0分鐘不付款自動取消,這個任務(wù)就是一個延時任務(wù)?!?/p>
一、DelayQueue的應(yīng)用原理
DelayQueue是一個無界的BlockingQueue的實現(xiàn)類,用于放置實現(xiàn)了Delayed接口的對象,其中的對象只能在其到期時才能從隊列中取走。
- BlockingQueue即阻塞隊列,java提供的面向多線程安全的隊列數(shù)據(jù)結(jié)構(gòu),當隊列內(nèi)元素數(shù)量為0的時候,試圖從隊列內(nèi)獲取元素的線程將被阻塞或者拋出異常。
- 這里的“無界”隊列,是指隊列的元素數(shù)量不存在上限,隊列的容量會隨著元素數(shù)量的增加而擴容。
DelayQueue實現(xiàn)了BlockingQueue接口,所以具有無界、阻塞的特點,除此之外它自己的核心特點就是:
- 「放入該隊列的延時任務(wù)對象,只要到達延時時間之后才能被取到」。
- DelayQueue 不接收null元素
- 「DelayQueue 只接受那些實現(xiàn)了java.util.concurrent.Delayed接口的對象」
二、訂單延時任務(wù)的實現(xiàn)
了解了DelayQueue的特點之后,我們就可以利用它來實現(xiàn)延時任務(wù)了,實現(xiàn)java.util.concurrent.Delayed接口。
import?org.jetbrains.annotations.NotNull; import?java.text.SimpleDateFormat; import?java.util.Date; import?java.util.concurrent.Delayed; import?java.util.concurrent.TimeUnit; /** ?*?延時訂單任務(wù) ?*/ public?class?OrderDelayObject?implements?Delayed?{ ??private?String?name; ??private?long?delayTime;???//延時時間 ??//實際業(yè)務(wù)中這里傳訂單信息對象,我這里只做demo,所以使用字符串了 ??private?String?order; ??public?OrderDelayObject(String?name,?long?delayTime,?String?order)?{ ????this.name?=?name; ????//延時時間加上當前時間 ????this.delayTime?=?System.currentTimeMillis()?+?delayTime; ????this.order?=?order; ??} ??//獲取延時任務(wù)的倒計時時間 ??@Override ??public?long?getDelay(TimeUnit?unit)?{ ????long?diff?=?delayTime?-?System.currentTimeMillis(); ????return?unit.convert(diff,?TimeUnit.MILLISECONDS); ??} ??//延時任務(wù)隊列,按照延時時間元素排序,實現(xiàn)Comparable接口 ??@Override ??public?int?compareTo(@NotNull?Delayed?obj)?{ ????return?Long.compare(this.delayTime,?((OrderDelayObject)?obj).delayTime); ??} ??@Override ??public?String?toString()?{ ????Date?date?=?new?Date(delayTime); ????SimpleDateFormat?sd?=?new?SimpleDateFormat("yyyy-MM-dd?HH:mm:ss"); ????return?"\nOrderDelayObject:{" ????????????+?"name="?+?name ????????????+?",?time="?+?sd.format(date) ????????????+?",?order="?+?order ????????????+?"}"; ??} }?
上文類中的order為訂單信息對象,在實際的業(yè)務(wù)開發(fā)過程中應(yīng)該是傳遞訂單信息,用于取消訂單業(yè)務(wù)的實現(xiàn)(訂單30分鐘不付款自動取消)。
Delayed接口繼承自 Comparable接口,所以需要實現(xiàn)compareTo方法,用于延時任務(wù)在隊列中按照“延時時間”進行排序。
getDelay方法是Delayed接口方法,實現(xiàn)該方法提供獲取延時任務(wù)的倒計時時間
三、訂單處理
首先我們需要一個容器,永久保存延時任務(wù)隊列,如果是Spring開發(fā)環(huán)境我們可以這樣做。
@Bean("orderDelayQueue") public DelayQueue<OrderDelayObject> orderDelayQueue(){ return new DelayQueue<OrderDelayObject>(); }
當用戶下單的時候,將訂單下單任務(wù)放入延時隊列
@Resource private?DelayQueue<OrderDelayObject>?orderDelayQueue; //發(fā)起訂單下單的時候?qū)⒂唵窝菔緦ο蠓湃雘rderDelayQueue orderDelayQueue.add( ????????new?OrderDelayObject( ????????????????"訂單延時取消任務(wù)", ????????????????30?*?60?*?1000,????//延時30分鐘 ????????????????"延時任務(wù)訂單對象信息" ????????) );
系統(tǒng)內(nèi)開啟一個線程,不斷的從隊列中獲取消息,獲取到之后對延時消息進行處理。DelayQueue的take方法從隊列中獲取延時任務(wù)對象,如果隊列元素數(shù)量為0,或者沒有到達“延時時間的任務(wù)”,該線程會被阻塞。
@Component public class DelayObjectConsumer<OrderDelayObject> implements InitializingBean { ? @Resource ? private DelayQueue<OrderDelayObject> orderDelayQueue; ? @Override ? public void afterPropertiesSet() throws Exception { ? ? new Thread(() -> { ? ? ? while (true) { ? ? ? ? OrderDelayObject task = orderDelayQueue.take(); ? ? ? ? System.out.println(task.toString()); ? ? ? ? System.out.println(task.getOrder()); ? ? ? ? //根據(jù)order訂單信息,去查詢該訂單的支付信息 ? ? ? ? //如果用戶沒有進行支付,將訂單從數(shù)據(jù)庫中關(guān)閉 ? ? ? ? //如果訂單并發(fā)量比較大,這里可以考慮異步或線程池的方式進行處理 ? ? ? } ? ? }).start(); ? } }
需要說明的是,這里的while-true循環(huán)的延時任務(wù)處理是順序執(zhí)行的,在訂單并發(fā)量比較大的時候,需要考慮異步處理的方式完成訂單的關(guān)閉操作。我之前寫過一個SpringBoot的可觀測、易配置的線程池開源項目,可能會對你有幫助,源代碼地址:gitee.com/hanxt/zimug-monitor-threadpool
經(jīng)過我的測試,放入orderDelayQueue的延時任務(wù),在半小時之后得到正確的執(zhí)行處理。說明我們的實現(xiàn)是正確的。
四、優(yōu)缺點
- 使用DelayQueue實現(xiàn)延時任務(wù)非常簡單,而且簡便,全部都是標準的JDK代碼實現(xiàn),不用引入第三方依賴(不依賴redis實現(xiàn)、消息隊列實現(xiàn)等),非常的輕量級。
- 它的缺點就是所有的操作都是基于應(yīng)用內(nèi)存的,一旦出現(xiàn)應(yīng)用單點故障,可能會造成延時任務(wù)數(shù)據(jù)的丟失。如果訂單并發(fā)量非常大,因為DelayQueue是無界的,訂單量越大,隊列內(nèi)的對象就越多,可能造成OOM的風險。所以使用DelayQueue實現(xiàn)延時任務(wù),只適用于任務(wù)量較小的情況。
到此這篇關(guān)于java通過DelayQueue實現(xiàn)延時任務(wù)的文章就介紹到這了,更多相關(guān)java DelayQueue延時任務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
myeclipse安裝Spring Tool Suite(STS)插件的方法步驟
這篇文章主要介紹了myeclipse安裝Spring Tool Suite(STS)插件的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧2019-08-08Java數(shù)據(jù)結(jié)構(gòu)與算法之雙向鏈表、環(huán)形鏈表及約瑟夫問題深入理解
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)與算法之雙向鏈表、環(huán)形鏈表及約瑟夫問題深入理解,本文給大家介紹的非常詳細,對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09idea新建mapper.xml文件詳細步驟如:mybatis-config
這篇文章主要介紹了idea新建xml模板設(shè)置,例如:mybatis-config,本文分步驟通過圖文并茂的形式給大家介紹的非常詳細,需要的朋友可以參考下2023-07-07java手動實現(xiàn)常見數(shù)據(jù)結(jié)構(gòu)的示例代碼
本文介紹了Java中常用數(shù)據(jù)結(jié)構(gòu)的特點和Java實現(xiàn),包括數(shù)組、動態(tài)數(shù)組、鏈表、棧、隊列、哈希表、樹、堆、圖、集合、雙向隊列以及自定義鏈表,幫助開發(fā)者選擇合適的數(shù)據(jù)結(jié)構(gòu)以提升代碼效率,感興趣的朋友一起看看吧2025-02-02JDK8接口的默認與靜態(tài)方法-接口與抽象類的區(qū)別詳解
這篇文章主要介紹了JDK8接口的默認與靜態(tài)方法-接口與抽象類的區(qū)別詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,,需要的朋友可以參考下2019-06-06Spring cloud Feign 深度學(xué)習與應(yīng)用詳解
這篇文章主要介紹了Spring cloud Feign 深度學(xué)習與應(yīng)用詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-06-06