Java?DelayQueue實現(xiàn)延時任務(wù)的示例詳解
一、DelayQueue的應(yīng)用原理
DelayQueue是一個無界的BlockingQueue的實現(xiàn)類,用于放置實現(xiàn)了Delayed接口的對象,其中的對象只能在其到期時才能從隊列中取走。
BlockingQueue即阻塞隊列,java提供的面向多線程安全的隊列數(shù)據(jù)結(jié)構(gòu),當(dāng)隊列內(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;
//延時時間加上當(dāng)前時間
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>();
}當(dāng)用戶下單的時候,將訂單下單任務(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 implements InitializingBean {
@Resource
private DelayQueue<OrderDelayObject> orderDelayQueue;
@Override
public void afterPropertiesSet() throws Exception {
while (true) {
OrderDelayObject task = orderDelayQueue.take();
System.out.println(task.toString());
System.out.println(task.getOrder());
//根據(jù)order訂單信息,去查詢該訂單的支付信息
//如果用戶沒有進行支付,將訂單從數(shù)據(jù)庫中關(guān)閉
//如果訂單并發(fā)量比較大,這里可以考慮異步或線程池的方式進行處理
}
}
}需要說明的是,這里的while-true循環(huán)的延時任務(wù)處理是順序執(zhí)行的,在訂單并發(fā)量比較大的時候,需要考慮異步處理的方式完成訂單的關(guān)閉操作。我之前寫過一個SpringBoot的可觀測、易配置的線程池開源項目,可能會對你有幫助,源代碼地址
經(jīng)過我的測試,放入orderDelayQueue的延時任務(wù),在半小時之后得到正確的執(zhí)行處理。說明我們的實現(xiàn)是正確的。
四、優(yōu)缺點
使用DelayQueue實現(xiàn)延時任務(wù)非常簡單,而且簡便,全部都是標(biāo)準(zhǔn)的JDK代碼實現(xiàn),不用引入第三方依賴(不依賴redis實現(xiàn)、消息隊列實現(xiàn)等),非常的輕量級。
它的缺點就是所有的操作都是基于應(yīng)用內(nèi)存的,一旦出現(xiàn)應(yīng)用單點故障,可能會造成延時任務(wù)數(shù)據(jù)的丟失。如果訂單并發(fā)量非常大,因為DelayQueue是無界的,訂單量越大,隊列內(nèi)的對象就越多,可能造成OOM的風(fēng)險。所以使用DelayQueue實現(xiàn)延時任務(wù),只適用于任務(wù)量較小的情況。
到此這篇關(guān)于Java DelayQueue實現(xiàn)延時任務(wù)的示例詳解的文章就介紹到這了,更多相關(guān)Java DelayQueue延時任務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Boot中@Validated注解不生效問題匯總大全
這篇文章主要給大家介紹了關(guān)于Spring?Boot中@Validated注解不生效問題匯總的相關(guān)資料,@Validated注解是Spring框架中的一個注解,用于在方法參數(shù)上添加參數(shù)校驗規(guī)則,需要的朋友可以參考下2023-07-07
SpringMVC使用@ExceptionHandler注解在Controller中處理異常
這篇文章主要為大家介紹了SpringMVC使用@ExceptionHandler注解在Controller中處理異常示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10
Spring中InitializingBean的使用詳細(xì)解析
這篇文章主要介紹了Spring中InitializingBean的使用詳細(xì)解析,InitializingBean是Spring提供的拓展性接口,提供了屬性初始化后的處理方法,它只有一個afterPropertiesSet方法,凡是繼承該接口的類,在bean的屬性初始化后都會執(zhí)行該方法,需要的朋友可以參考下2024-02-02
JavaWeb dbutils執(zhí)行sql命令并遍歷結(jié)果集時不能查到內(nèi)容的原因分析
這篇文章主要介紹了JavaWeb dbutils執(zhí)行sql命令并遍歷結(jié)果集時不能查到內(nèi)容的原因分析及簡單處理方法,文中給大家介紹了javaweb中dbutils的使用,需要的朋友可以參考下2017-12-12

