DelayQueue简介
DelayQueue是JUC包(java.util.concurrent)为我们提供的延迟队列,它是一个基于PriorityQueue实现的一个无界队列,是一个线程安全的延迟队列。
关于PriorityQueue可以参考笔者编写的这篇文章:
当我们希望某个任务在某个时间才能取出并操作时,我们就可以让这个继承Delayed接口,实现其计算任务到期时间的方法 getDelay 。然后将任务存放到 DelayQueue 中,默认情况下, DelayQueue 会按照到期时间升序编排任务。随后当 DelayQueue 发现任务到期时,我们才能从 DelayQueue 中取出这个任务并执行。
这使得 DelayQueue非常适合运用于以下两种场景:
- 定时任务 : DelayQueue 非常适合用于处理那些到期才能执行的任务,例如用户触发下单请求,我们规定15min后未支付则取消订单,那么我们就可以提交一个15min后到查询用户下单情况的任务给DelayQueue,如果15min后取出该任务发现用户还未下单,则取消这个订单。
- 缓存过期 : 假如我们使用Java维护一个内存,我们希望缓存具备时效性,同样我们可以封装一个缓存过期删除的任务提交到DelayQueue,DelayQueue会在到期后取出这个任务并将缓存数据删除。
DelayQueue发展史
DelayQueue 最早是在 Java 5 中引入的,作为 java.util.concurrent 包中的一部分,用于支持基于时间的任务调度和缓存过期删除等场景,该版本仅仅支持延迟功能的实现,还未解决线程安全问题。
在 Java 6 中,DelayQueue 的实现进行了优化,通过使用 ReentrantLock 和 Condition 解决线程安全及线程间交互的效率,提高了其性能和可靠性。
在 Java 7 中,DelayQueue 的实现进行了进一步的优化,通过使用 CAS 操作实现元素的添加和移除操作,提高了其并发操作性能。
在 Java 8 中,DelayQueue 的实现没有进行重大变化,但是在 java.time 包中引入了新的时间类,如 Duration 和 Instant,使得使用 DelayQueue 进行基于时间的调度更加方便和灵活。
在 Java 9 中,DelayQueue 的实现进行了一些微小的改进,主要是对代码进行了一些优化和精简。
总的来说,DelayQueue 的发展史主要是通过优化其实现方式和提高其性能和可靠性,使其更加适用于基于时间的调度和缓存过期删除等场景。
DelayQueue常见使用场景示例
定时任务
我们希望任务可以按照我们预期的时间执行,例如提交3个任务,分别要求1s、2s、3s后执行,即使是乱序添加,1s后要求1s执行的任务会准时执行。
对此我们可以使用DelayQueue来实现,所以我们首先需要继承Delayed实现 DelayedTask,实现getDelay方法以及优先级比较compareTo。
/**
* 延迟任务
*/
public class DelayedTask implements Delayed {
/**
* 任务到期时间
*/
private long executeTime;
/**
* 任务
*/
private Runnable task;
public DelayedTask(long delay, Runnable task) {
this.executeTime = System.currentTimeMillis() + delay;
this.task = task;
}
/**
* 查看当前任务还有多久到期
* @param unit
* @return
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(executeTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
/**
* 延迟队列需要到期时间升序入队,所以我们需要实现compareTo进行到期时间比较
* @param o
* @return
*/
@Override
public int compareTo(Delayed o) {
return Long.compare(this.executeTime, ((DelayedTask) o).executeTime);
}
public void execute() {
task.run();
}
}
完成任务的封装之后,使用就很简单了,设置好多久到期然后将任务提交到延迟队列中即可。
public static void main(String[] args) throws InterruptedException {
// 创建延迟队列,并添加任务
DelayQueue<DelayedTask> delayQueue = new DelayQueue<>();
//分别添加1s、2s、3s到期的任务
delayQueue.add(new DelayedTask(2000, () -> System.out.println("Task 2")));
delayQueue.add(new DelayedTask(1000, () -> System.out.println("Task 1")));
delayQueue.add(new DelayedTask(3000, () -> System.out.println("Task 3")));
// 取出任务并执行
while (!delayQueue.isEmpty()) {
//阻塞获取最先到期的任务
DelayedTask task = delayQueue.take();
if (task != null) {
task.execute();