DelayQueue是一個支持延時獲取元素的無界阻塞隊列。隊列中的元素必須實現Delayed接口,在創建元素的時候可以指定多久才能從隊列中獲取當前元素,只有在延遲期滿時才能從隊列中獲取元素。
我們可以將DelayQueue運用在以下應用場景:
緩存系統的設計:可以用DelayQueue保存緩存元素的有效期,使用一個線程循環查詢DelayQueue,一旦能從DelayQueue中獲取元素時,表示緩存有效期到了。
定時任務調度:使用DelayQueue保存當天將會執行的任務和執行時間,一旦從DelayQueue中獲取到任務就開始執行,從比如TimerQueue就是使用DelayQueue實現的。
——以上摘自Java并發編程的藝術
下面是自已擼的一個小栗子
1、用于執行通知任務的隊列元素
packagecom.dreyer.concurrent;
importjava.util.concurrent.Delayed;
importjava.util.concurrent.TimeUnit;
/**
*@description通知延遲隊列
*@author:Dreyer
*@date:16/4/27 下午2:40
*/
public classNotifyTaskimplementsRunnable,Delayed {
// 通知名稱
privateStringnotifyTaskName;
// 執行時間
private longexecuteTime;
publicNotifyTask(String notifyTaskName, longexecuteTime) {
this.notifyTaskName= notifyTaskName;
this.executeTime= executeTime;
}
/**
* 設置延遲時間
*@paramunit
*@return
*/
public longgetDelay(TimeUnit unit) {
returnunit.convert(executeTime- System.currentTimeMillis(),unit.MILLISECONDS);
}
/**
* 用來指定元素的順序,讓延時時間最長的放在隊列的末尾
*@paramo
*@return
*/
public intcompareTo(Delayed o) {
NotifyTask notifyTask = (NotifyTask) o;
returnexecuteTime> notifyTask.executeTime?1: (executeTime< notifyTask.executeTime? -1:0);
}
public voidrun() {
System.out.println("當前時間毫秒數:"+ System.currentTimeMillis() +","+this.toString() +"正在執行...");
}
@Override
publicStringtoString() {
return"NotifyTask{"+
"notifyTaskName='"+notifyTaskName+'\''+
", executeTime="+executeTime+
'}';
}
}
2、測試類
packagecom.dreyer.concurrent;
importjava.util.Random;
importjava.util.concurrent.DelayQueue;
/**
*@description測試類
*@author:Dreyer
*@date:16/4/27 下午3:56
*/
public classNotifyTaskTest {
/**
* 通知任務存放的延時隊列
*/
public staticDelayQueuetasks=newDelayQueue();
public static voidmain(String[] args) {
Random random =newRandom();
for(inti =0;i <5;i++) {
// 隨機產生一個秒數
intseconds = random.nextInt(5) +1;
NotifyTask notifyTask =newNotifyTask("任務"+ i,System.currentTimeMillis() + (seconds *1000));
tasks.put(notifyTask);
}
longstart = 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");
}
}
3、執行結果(部分)
......
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就能很好的實現這個功能了。