DelayQueue是一個支持延時獲取元素的無界阻塞隊列。隊列中的元素必須實現Delayed接口,在建立元素的時候能夠指定多久才能從隊列中獲取當前元素,只有在延遲期滿時才能從隊列中獲取元素。java
咱們能夠將DelayQueue運用在如下應用場景:編程
緩存系統的設計:能夠用DelayQueue保存緩存元素的有效期,使用一個線程循環查詢DelayQueue,一旦能從DelayQueue中獲取元素時,表示緩存有效期到了。緩存
定時任務調度:使用DelayQueue保存當天將會執行的任務和執行時間,一旦從DelayQueue中獲取到任務就開始執行,從好比TimerQueue就是使用DelayQueue實現的。併發
——以上摘自Java併發編程的藝術dom
下面是自已擼的一個小栗子
ide
一、用於執行通知任務的隊列元素
函數
package com.dreyer.concurrent; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; /** * @description 通知延遲隊列 * @author: Dreyer * @date: 16/4/27 下午2:40 */ public class NotifyTask implements Runnable, Delayed { // 通知名稱 private String notifyTaskName; // 執行時間 private long executeTime; public NotifyTask(String notifyTaskName, long executeTime) { this.notifyTaskName = notifyTaskName; this.executeTime = executeTime; } /** * 設置延遲時間 * @param unit * @return */ public long getDelay(TimeUnit unit) { return unit.convert(executeTime - System.currentTimeMillis(), unit.MILLISECONDS); } /** * 用來指定元素的順序,讓延時時間最長的放在隊列的末尾 * @param o * @return */ public int compareTo(Delayed o) { NotifyTask notifyTask = (NotifyTask) o; return executeTime > notifyTask.executeTime ? 1 : (executeTime < notifyTask.executeTime ? -1 : 0); } public void run() { System.out.println("當前時間毫秒數:" + System.currentTimeMillis() + "," + this.toString() + "正在執行..."); } @Override public String toString() { return "NotifyTask{" + "notifyTaskName='" + notifyTaskName + '\'' + ", executeTime=" + executeTime + '}'; } }
二、測試類測試
package com.dreyer.concurrent; import java.util.Random; import java.util.concurrent.DelayQueue; /** * @description 測試類 * @author: Dreyer * @date: 16/4/27 下午3:56 */ public class NotifyTaskTest { /** * 通知任務存放的延遲隊列 */ public static DelayQueue<NotifyTask> tasks = new DelayQueue<NotifyTask>(); public static void main(String[] args) { Random random = new Random(); for (int i = 0; i < 5; i++) { // 隨機產生一個秒數 int seconds = random.nextInt(5) + 1; NotifyTask notifyTask = new NotifyTask("任務" + i,System.currentTimeMillis() + (seconds * 1000)); tasks.put(notifyTask); } long start = System.currentTimeMillis(); while (true) { NotifyTask notifyTask = tasks.poll(); if (notifyTask != null) { notifyTask.run(); } // 若是隊列中的元素所有被取完,則跳出循環 if (tasks.size() == 0) { break; } System.out.println("is running......"); } System.out.println("耗時:" + (System.currentTimeMillis() - start) / 1000 + "ms"); } }
三、執行結果(部分)this
......spa
is running......
is running......
is running......
is running......
is running......
is running......
is running......
is running......
is running......
is running......
當前時間毫秒數:1461762721192,NotifyTask{notifyTaskName='任務0', executeTime=1461762721192}正在執行...
is running......
當前時間毫秒數:1461762721192,NotifyTask{notifyTaskName='任務1', executeTime=1461762721192}正在執行...
耗時:5ms
從結果中,咱們能夠看出NotifyTask的executeTime是設置在何時執行,那它的執行時間也會是那個時候
注意點:
栗子中的構造函數設置的延遲時間參數executeTime的單位是毫秒,getDelay()方法能夠指定任意單位(栗子中指定的是毫秒),建議不要使用值過大的單位,好比秒 / 分,若是getDelay()方法返回的是一個負數,那隊列中能當即獲取到元素。
關於DelayQueue在公司項目裏面的應用場景主要是:
訂單支付成功或者失敗後要給商戶發送相應的通知,針對同一條通知記錄,若是是第一次發,則須要等待的時間是0分鐘,第二次發則須要等待1分鐘,第三次發則須要等待3分鐘,即發送次數每+1,則須要等待的時長也要相應的增長,那使用DelayQueue就能很好的實現這個功能了。