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就能很好的实现这个功能了。