Java DelayQueue实现延时队列

DelayQueue 是 Java 中的一个特殊队列,它是一个无界阻塞延时队列,用于存放实现了 Delayed 接口的元素。这些元素只有在其指定的延时时间到期后才能被取出。DelayQueue 的内部是使用优先级堆来实现的,可以确保队列中的元素按照延时时间的顺序排列。

DelayQueue 的主要特点和作用包括:

  1. 延时元素存储: DelayQueue 存放的元素必须是实现了 Delayed 接口的对象。每个元素都有一个延时时间,只有在延时时间到期后才能被取出。

  2. 按延时时间排序: 队列中的元素按照延时时间的顺序排列,即延时时间最短的元素位于队列的头部,延时时间最长的元素位于队列的尾部。

  3. 阻塞操作: DelayQueue 提供了阻塞式的取元素操作,如果队列为空,调用 take() 方法将会阻塞直到有元素可以取出。

  4. 线程安全: DelayQueue 是线程安全的,可以在多线程环境下安全地使用。

DelayQueue 的典型应用场景包括:

  • 延时任务调度: 可以将需要延时执行的任务封装成实现 Delayed 接口的对象,然后放入 DelayQueue 中进行调度和执行。
  • 缓存过期清理: 可以将缓存中的对象封装成实现 Delayed 接口的对象,并设置它们的过期时间,然后放入 DelayQueue 中。通过定期从队列中取出过期的对象进行清理,可以实现缓存的过期清理功能。

总的来说,DelayQueue 提供了一种方便的机制来实现延时任务调度和处理延时元素,是 Java 多线程编程中的重要组件之一。

1、通用延时任务类

/**
 * @author Lucas
 * date 2024/4/8 21:13
 * description 延时任务
 */
@Data
public class DelayTask<T> implements Delayed {
    /**
     * 消息内容
     */
    private T data;

    /**
     * 执行时间戳
     */
    private long expire;

    /**
     * 剩余时间 = 执行时间 - 当前时间
     * @param unit the time unit
     * @return
     */
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.expire - System.currentTimeMillis(), unit);
    }

    /**
     * 优先级规则:两个任务比较,时间短的优先执行
     * @param o the object to be compared.
     * @return
     */
    @Override
    public int compareTo(Delayed o) {
        long delta = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
        return (int) delta;
    }
}

2、消息内容类(根据自身业务场景定义)

/**
 * @author Lucas
 * date 2024/4/8 21:21
 * description 消息内容(根据自身业务场景定义)
 */
@Data
public class TaskBase {
    /**
     * ID
     */
    private Integer id;

    /**
     * 名称
     */
    private String name;
}

3、延迟队列管理器类

/**
 * @author Lucas
 * date 2024/4/8 21:21
 * description 延迟队列管理器
 */
@Slf4j
@Configuration
public class DelayQueueManager<T> implements CommandLineRunner {

    private DelayQueue<DelayTask> delayQueue = new DelayQueue<>();

    /**
     * 加入延时队列
     * @param message   消息内容
     * @param expire    执行时间戳
     */
    public void put(T message, long expire) {
        log.info("加入DelayQueue延时任务,消息内容:{}", JSONObject.toJSONString(message));
        DelayTask delayTask = new DelayTask();
        delayTask.setData(message);
        delayTask.setExpire(expire);
        delayQueue.put(delayTask);
    }

    /**
     * 取消延时任务
     * @param message   消息内容
     * @param expire    执行时间戳
     * @return
     */
    public boolean remove(T message, long expire) {
        log.info("取消DelayQueue延时任务,消息内容:{}", JSONObject.toJSONString(message));
        DelayTask delayTask = new DelayTask();
        delayTask.setData(message);
        delayTask.setExpire(expire);
        return delayQueue.remove(delayTask);
    }

    /**
     * 推送的消息内容可能持久化到数据库,避免系统重启导致消息丢失
     * 系统重启在此获取数据重新加载到队列中
     * @param args incoming main method arguments
     * @throws Exception
     */
    @Override
    public void run(String... args) throws Exception {
        log.info("服务重启,初始化延时队列信息");
        //单线程的线程池中执行,适用于需要顺序执行任务或者保证线程安全的场景
        Executors.newSingleThreadExecutor().execute(new Thread(this::executeThread));
    }

    /**
     * 延时任务执行线程
     */
    private void executeThread() {
        while (true) {
            try {
                DelayTask task = delayQueue.take();
                log.info("执行延时任务,内容:{}", JSONObject.toJSONString(task));
            } catch (InterruptedException e) {
                break;
            }
        }
    }
}

4、测试(根据自身业务场景实现)

private final DelayQueueManager<TaskBase> delayQueueManager;
        

TaskBase taskBase = new TaskBase();
taskBase.setId(123);
taskBase.setName("Test1234567890");
long date =  new Date().getTime() + 10000;
//加入延时队列
delayQueueManager.put(taskBase,date);
//移除延时队列
delayQueueManager.remove(taskBase,date);

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值