【夜话系列】DelayQueue延迟队列(上):原理剖析与实现机制

🔥 本文是DelayQueue系列的上篇,主要聚焦延迟队列的基础概念和实现原理。通过循序渐进的讲解,带你深入理解DelayQueue的核心机制和内部实现。

📚博主匠心之作,强推专栏

在这里插入图片描述

一、认识DelayQueue

1.1 DelayQueue简介

  • DelayQueue是Java并发包下的延时阻塞队列
  • 继承自BlockingQueue接口,提供了阻塞式访问
  • 只有延迟期满的元素才能被取出
  • 队列头部是延迟最小的元素

DelayQueue本质上是一个支持延时获取元素的无界阻塞队列,队列中的元素必须实现Delayed接口。在创建元素时可以指定多久才能从队列中获取当前元素,只有在延迟期满时才能从队列中提取元素。

DelayQueue的基本定义:

public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
    implements BlockingQueue<E> {
   
    // ...实现细节
}

1.2 核心特性

  • 线程安全:内部使用ReentrantLock保证并发安全
  • 延时功能:元素必须实现Delayed接口
  • 优先级排序:底层使用PriorityQueue实现
  • 阻塞特性:支持阻塞式获取和添加操作

DelayQueue的核心在于Delayed接口,所有放入队列的元素都必须实现这个接口。Delayed接口继承了Comparable接口,这意味着元素不仅要能计算剩余延迟时间,还需要支持排序功能,以便队列能够按照延迟时间的先后顺序进行排列。

Delayed接口定义:

public interface Delayed extends Comparable<Delayed> {
   
    // 获取剩余延迟时间
    long getDelay(TimeUnit unit);
}

getDelay方法返回的是剩余延迟时间,如果返回0或负数,则表示延迟已经到期,元素可以被取出。

1.3 快速入门示例

下面通过一个简单的示例来展示DelayQueue的基本用法。首先,我们需要创建一个实现了Delayed接口的任务类:

  1. 创建延时任务:
public class DelayedTask implements Delayed {
   
    private String taskName;
    private long executeTime; // 任务执行时间

    public DelayedTask(String taskName, long delayTime) {
   
        this.taskName = taskName;
        this.executeTime = System.currentTimeMillis() + delayTime;
    }

    @Override
    public long getDelay(TimeUnit unit) {
   
        return unit.convert(executeTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
   
        return Long.compare(this.executeTime, ((DelayedTask) o).executeTime);
    }

    @Override
    public String toString() {
   
        return "DelayedTask{taskName='" + taskName + "', executeTime=" + executeTime + "}";
    }
}

在这个DelayedTask类中:

  • taskName:任务名称,用于标识任务
  • executeTime:任务的执行时间,等于当前时间加上延迟时间
  • getDelay():计算剩余延迟时间,当返回值小于等于0时,表示任务可以执行了
  • compareTo():用于在队列中按照执行时间排序,确保最先到期的任务在队列头部

接下来,我们使用DelayQueue来管理这些延时任务:

  1. 使用DelayQueue:
public class DelayQueueDemo {
   
    public static void main(String[] args) throws InterruptedException {
   
        // 创建延时队列
        DelayQueue<DelayedTask> delayQueue = new DelayQueue<>();
        
        // 添加延时任务
        delayQueue.offer(new DelayedTask("任务1", 2000)); // 2秒后执行
        delayQueue.offer(new DelayedTask("任务2", 1000)); // 1秒后执行
        delayQueue.offer(new DelayedTask("任务3", 3000)); // 3秒后执行

        // 获取任务执行
        while (!delayQueue.isEmpty()) {
   
            DelayedTask task = delayQueue.take(); // 按延时时间顺序获取任务
            System.out.println("执行任务:" + task);
        }
    }
}

/* 运行结果:
执行任务:DelayedTask{taskName='任务2', executeTime=1703123456789}
执行任务:DelayedTask{taskName='任务1', executeTime=1703123457789}
执行任务:DelayedTask{taskName='任务3', executeTime=1703123458789}
*/

在这个示例中:

  • 我们创建了一个DelayQueue实例,用于存放DelayedTask对象
  • 向队列中添加了三个不同延迟时间的任务
  • 使用take()方法获取任务,这个方法会阻塞直到有任务到期
  • 任务按照延迟时间的先后顺序被取出执行,而不是按照添加顺序

从运行结果可以看出,虽然任务1先添加,但任务2的延迟时间更短,所以任务2先被执行。这正是DelayQueue的核心特性:按照延迟到期时间排序,而不是按照添加顺序。

1.4 典型应用场景

  • 订单超时自动取消
  • 限时优惠券管理
  • 缓存数据过期清理
  • 定时任务调度
  • 消息延时投递

DelayQueue在实际项目中有很多应用场景,最典型的就是需要在一定时间后执行的任务。下面以订单超时自动取消为例,展示DelayQueue的实际应用:

订单超时自动取消示例:

public class OrderTimeoutExample {
   
    private static final DelayQueue<DelayedOrder> ORDER_QUEUE = new DelayQueue<>();
    
    public static void main(String[]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值