DelayQueue簡介html
在不少場景咱們須要用到延時任務,好比給客戶異步轉帳操做超時後發通知告知用戶,還有客戶下單後多長時間內沒支付則取消訂單等等,這些均可以使用延時任務來實現。java
jdk中DelayQueue能夠實現上述需求,顧名思義DelayQueue就是延時隊列。spring
DelayQueue提供了在指定時間才能獲取隊列元素的功能,隊列頭元素是最接近過時的元素。json
沒有過時元素的話,使用poll()方法會返回null值,超時斷定是經過getDelay(TimeUnit.NANOSECONDS)方法的返回值小於等於0來判斷。springboot
延時隊列不能存放空元素。異步
通常使用take()方法阻塞等待,有過時元素時繼續。ide
隊列元素說明this
DelayQueue<E extends Delayed>的隊列元素須要實現Delayed接口,該接口類定義以下:spa
public interface Delayed extends Comparable<Delayed> { /** * Returns the remaining delay associated with this object, in the * given time unit. * * @param unit the time unit * @return the remaining delay; zero or negative values indicate * that the delay has already elapsed */ long getDelay(TimeUnit unit); }
因此DelayQueue的元素須要實現getDelay方法和Comparable接口的compareTo方法,getDelay方法來斷定元素是否過時,compareTo方法來肯定前後順序。線程
springboot中實例運用
DelayTask就是隊列中的元素
import java.util.Date; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; public class DelayTask implements Delayed { final private TaskBase data; final private long expire; /** * 構造延時任務 * @param data 業務數據 * @param expire 任務延時時間(ms) */ public DelayTask(TaskBase data, long expire) { super(); this.data = data; this.expire = expire + System.currentTimeMillis(); } public TaskBase getData() { return data; } public long getExpire() { return expire; } @Override public boolean equals(Object obj) { if (obj instanceof DelayTask) { return this.data.getIdentifier().equals(((DelayTask) obj).getData().getIdentifier()); } return false; } @Override public String toString() { return "{" + "data:" + data.toString() + "," + "expire:" + new Date(expire) + "}"; } @Override public long getDelay(TimeUnit unit) { return unit.convert(this.expire - System.currentTimeMillis(), unit); } @Override public int compareTo(Delayed o) { long delta = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS); return (int) delta; } }
TaskBase類是用戶自定義的業務數據基類,其中有一個identifier字段來標識任務的id,方便進行索引
import com.alibaba.fastjson.JSON; public class TaskBase { private String identifier; public TaskBase(String identifier) { this.identifier = identifier; } public String getIdentifier() { return identifier; } public void setIdentifier(String identifier) { this.identifier = identifier; } @Override public String toString() { return JSON.toJSONString(this); } }
定義一個延時任務管理類DelayQueueManager,經過@Component註解加入到spring中管理,在須要使用的地方經過@Autowire注入
import com.alibaba.fastjson.JSON; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.DelayQueue; import java.util.concurrent.Executors; @Component public class DelayQueueManager implements CommandLineRunner { private final Logger logger = LoggerFactory.getLogger(DelayQueueManager.class); private DelayQueue<DelayTask> delayQueue = new DelayQueue<>(); /** * 加入到延時隊列中 * @param task */ public void put(DelayTask task) { logger.info("加入延時任務:{}", task); delayQueue.put(task); } /** * 取消延時任務 * @param task * @return */ public boolean remove(DelayTask task) { logger.info("取消延時任務:{}", task); return delayQueue.remove(task); } /** * 取消延時任務 * @param taskid * @return */ public boolean remove(String taskid) { return remove(new DelayTask(new TaskBase(taskid), 0)); } @Override public void run(String... args) throws Exception { logger.info("初始化延時隊列"); Executors.newSingleThreadExecutor().execute(new Thread(this::excuteThread)); } /** * 延時任務執行線程 */ private void excuteThread() { while (true) { try { DelayTask task = delayQueue.take(); processTask(task); } catch (InterruptedException e) { break; } } } /** * 內部執行延時任務 * @param task */ private void processTask(DelayTask task) { logger.info("執行延時任務:{}", task); //根據task中的data自定義數據來處理相關邏輯,例 if (task.getData() instanceof XXX) {} } }
DelayQueueManager實現了CommandLineRunner接口,在springboot啓動完成後就會自動調用run方法。
原文出處:https://www.cnblogs.com/ieinstein/p/12028459.html