DelayQueue是一個支持延時獲取元素的無界阻塞隊列。隊列中的元素必須實現(xiàn)Delayed接口,在創(chuàng)建元素的時候可以指定多久才能從隊列中獲取當(dāng)前元素,只有在延遲期滿時才能從隊列中獲取元素。
我們可以將DelayQueue運用在以下應(yīng)用場景:
緩存系統(tǒng)的設(shè)計:可以用DelayQueue保存緩存元素的有效期,使用一個線程循環(huán)查詢DelayQueue,一旦能從DelayQueue中獲取元素時,表示緩存有效期到了。
定時任務(wù)調(diào)度:使用DelayQueue保存當(dāng)天將會執(zhí)行的任務(wù)和執(zhí)行時間,一旦從DelayQueue中獲取到任務(wù)就開始執(zhí)行,從比如TimerQueue就是使用DelayQueue實現(xiàn)的。
——以上摘自Java并發(fā)編程的藝術(shù)
下面是自已擼的一個小栗子
1、用于執(zhí)行通知任務(wù)的隊列元素
packagecom.dreyer.concurrent;
importjava.util.concurrent.Delayed;
importjava.util.concurrent.TimeUnit;
/**
*@description通知延遲隊列
*@author:Dreyer
*@date:16/4/27 下午2:40
*/
public classNotifyTaskimplementsRunnable,Delayed {
// 通知名稱
privateStringnotifyTaskName;
// 執(zhí)行時間
private longexecuteTime;
publicNotifyTask(String notifyTaskName, longexecuteTime) {
this.notifyTaskName= notifyTaskName;
this.executeTime= executeTime;
}
/**
* 設(shè)置延遲時間
*@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("當(dāng)前時間毫秒數(shù):"+ System.currentTimeMillis() +","+this.toString() +"正在執(zhí)行...");
}
@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 {
/**
* 通知任務(wù)存放的延時隊列
*/
public staticDelayQueuetasks=newDelayQueue();
public static voidmain(String[] args) {
Random random =newRandom();
for(inti =0;i <5;i++) {
// 隨機產(chǎn)生一個秒數(shù)
intseconds = random.nextInt(5) +1;
NotifyTask notifyTask =newNotifyTask("任務(wù)"+ i,System.currentTimeMillis() + (seconds *1000));
tasks.put(notifyTask);
}
longstart = System.currentTimeMillis();
while(true) {
NotifyTask notifyTask =tasks.poll();
if(notifyTask !=null) {
notifyTask.run();
}
// 如果隊列中的元素全部被取完,則跳出循環(huán)
if(tasks.size() ==0) {
break;
}
System.out.println("is running......");
}
System.out.println("耗時:"+ (System.currentTimeMillis() - start) /1000+"ms");
}
}
3、執(zhí)行結(jié)果(部分)
......
is running......
is running......
is running......
is running......
is running......
is running......
is running......
is running......
is running......
is running......
當(dāng)前時間毫秒數(shù):1461762721192,NotifyTask{notifyTaskName='任務(wù)0', executeTime=1461762721192}正在執(zhí)行...
is running......
當(dāng)前時間毫秒數(shù):1461762721192,NotifyTask{notifyTaskName='任務(wù)1', executeTime=1461762721192}正在執(zhí)行...
耗時:5ms
從結(jié)果中,我們可以看出NotifyTask的executeTime是設(shè)置在什么時候執(zhí)行,那它的執(zhí)行時間也會是那個時候
注意點:
栗子中的構(gòu)造函數(shù)設(shè)置的延遲時間參數(shù)executeTime的單位是毫秒,getDelay()方法可以指定任意單位(栗子中指定的是毫秒),建議不要使用值過大的單位,比如秒 / 分,如果getDelay()方法返回的是一個負數(shù),那隊列中能立即獲取到元素。
關(guān)于DelayQueue在公司項目里面的應(yīng)用場景主要是:
訂單支付成功或者失敗后要給商戶發(fā)送相應(yīng)的通知,針對同一條通知記錄,如果是第一次發(fā),則需要等待的時間是0分鐘,第二次發(fā)則需要等待1分鐘,第三次發(fā)則需要等待3分鐘,即發(fā)送次數(shù)每+1,則需要等待的時長也要相應(yīng)的增加,那使用DelayQueue就能很好的實現(xiàn)這個功能了。