springboot执行延时任务-delayqueue的使用

依天照海      2022-05-19     147

关键词:

DelayQueue简介

在很多场景我们需要用到延时任务,比如给客户异步转账操作超时后发通知告知用户,还有客户下单后多长时间内没支付则取消订单等等,这些都可以使用延时任务来实现。

jdk中DelayQueue可以实现上述需求,顾名思义DelayQueue就是延时队列。

DelayQueue提供了在指定时间才能获取队列元素的功能,队列头元素是最接近过期的元素。

没有过期元素的话,使用poll()方法会返回null值,超时判定是通过getDelay(TimeUnit.NANOSECONDS)方法的返回值小于等于0来判断。

延时队列不能存放空元素。

一般使用take()方法阻塞等待,有过期元素时继续。

队列元素说明

DelayQueue<E extends Delayed>的队列元素需要实现Delayed接口,该接口类定义如下:

public interface Delayed extends Comparable<Delayed> {

    /**
     * Returns the remaining delay associated with this object, in the
     * given time unit.
     *
     * @param unit the time unit
     * @return the remaining delay; zero or negative values indicate
     * that the delay has already elapsed
     */
    long getDelay(TimeUnit unit);
}

所以DelayQueue的元素需要实现getDelay方法和Comparable接口的compareTo方法,getDelay方法来判定元素是否过期,compareTo方法来确定先后顺序。

springboot中实例运用

DelayTask就是队列中的元素

import java.util.Date;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class DelayTask implements Delayed {
    final private TaskBase data;
    final private long expire;

    /**
     * 构造延时任务
     * @param data      业务数据
     * @param expire    任务延时时间(ms)
     */
    public DelayTask(TaskBase data, long expire) {
        super();
        this.data = data;
        this.expire = expire + System.currentTimeMillis();
    }

    public TaskBase getData() {
        return data;
    }

    public long getExpire() {
        return expire;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof DelayTask) {
            return this.data.getIdentifier().equals(((DelayTask) obj).getData().getIdentifier());
        }
        return false;
    }

    @Override
    public String toString() {
        return "{" + "data:" + data.toString() + "," + "expire:" + new Date(expire) + "}";
    }

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

    @Override
    public int compareTo(Delayed o) {
        long delta = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
        return (int) delta;
    }
}
TaskBase类是用户自定义的业务数据基类,其中有一个identifier字段来标识任务的id,方便进行索引
import com.alibaba.fastjson.JSON;

public class TaskBase {
    private String identifier;

    public TaskBase(String identifier) {
        this.identifier = identifier;
    }

    public String getIdentifier() {
        return identifier;
    }

    public void setIdentifier(String identifier) {
        this.identifier = identifier;
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }
}

定义一个延时任务管理类DelayQueueManager,通过@Component注解加入到spring中管理,在需要使用的地方通过@Autowire注入

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Executors;

@Component
public class DelayQueueManager implements CommandLineRunner {
    private final Logger logger = LoggerFactory.getLogger(DelayQueueManager.class);
    private DelayQueue<DelayTask> delayQueue = new DelayQueue<>();
    
    /**
     * 加入到延时队列中
     * @param task
     */
    public void put(DelayTask task) {
        logger.info("加入延时任务:{}", task);
        delayQueue.put(task);
    }

    /**
     * 取消延时任务
     * @param task
     * @return
     */
    public boolean remove(DelayTask task) {
        logger.info("取消延时任务:{}", task);
        return delayQueue.remove(task);
    }

    /**
     * 取消延时任务
     * @param taskid
     * @return
     */
    public boolean remove(String taskid) {
        return remove(new DelayTask(new TaskBase(taskid), 0));
    }

    @Override
    public void run(String... args) throws Exception {
        logger.info("初始化延时队列");
        Executors.newSingleThreadExecutor().execute(new Thread(this::excuteThread));
    }

    /**
     * 延时任务执行线程
     */
    private void excuteThread() {
        while (true) {
            try {
                DelayTask task = delayQueue.take();
                processTask(task);
            } catch (InterruptedException e) {
                break;
            }
        }
    }

    /**
     * 内部执行延时任务
     * @param task
     */
    private void processTask(DelayTask task) {
        logger.info("执行延时任务:{}", task);
        //根据task中的data自定义数据来处理相关逻辑,例 if (task.getData() instanceof XXX) {}
    }
}

DelayQueueManager实现了CommandLineRunner接口,在springboot启动完成后就会自动调用run方法。

springboot使用rabbitmq实现延时任务(代码片段)

...务操作失败后,间隔一定的时间进行失败重试。本文基于springboot,使用rabbitmq_delayed_message_e 查看详情

juc并发编程共享模式之工具threadpoolexecutor--任务调度线程池定时任务/延时执行(scheduledthreadpoolexecutor延时执行/定时执行)(代(代码片段)

1.任务调度线程池1.1ScheduledThreadPoolExecutor延时执行示例代码(任务都延时1s执行):packagecom.tian;importjava.util.Date;importjava.util.concurrent.ExecutionException;importjava.util.concurrent.Executors;importj 查看详情

springboot中@scheduled和@async的使用

...区别:@Scheduled任务调度注解,主要用于配置定时任务;springboot默认的调度器线程池大小为1。注意:在spring中的@schedule默认的线程池中只有一个线程,所以如果在多个方法上加上@schedule的话,此时就会有多个任务加入到延时队列... 查看详情

kotlin延时执行任务操作(代码片段)

Timer().schedule(3000)//执行的任务 查看详情

java-延时执行-参数-任务

定义任务ScheduledExecutorServicescheduledExecutorService=Executors.newScheduledThreadPool(50);scheduledExecutorService.schedule(newDoorGuardDelUserThreadPool(vd,companyDao,preRegistrationDataDao,doorGuard 查看详情

分布式延时任务解决方案

...别定时任务有明确的触发时间,延时任务没有定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执行周期定时任务一般执行的是批处理操作是多 查看详情

ucosii如何分配任务之间的延时节拍时间

...务之间的延时节拍时间参考技术A我说的是UCOSII的周期性执行任务,不是时间分片。而周期性任务的结构如下:voidMyTask(void*pdata)//周期性执行的任务函数进行准备工作的代码;for(;;)//无限循环,也可用while(1)任务实体代码;OSTimeDly()... 查看详情

android延时执行某个任务

...个函数,或者是界面的跳转;在延时几秒之后再执行这个函数或者是界面的startActivity();这时候我们就需要做下延时处理以让用户看到某个操作效果,或者是隐含的操作!实现的方式有以下几种:第一种:/*... 查看详情

executors框架二scheduledthreadpoolexecutor线程池(代码片段)

用途:ScheduledThreadPoolExecutor(计划任务线程池)主要用于执行一些需要延时操作或者需要重复操作的任务,Spring框架自带计划任务功能场景一:延时操作,提供给客户统计数据功能,如果高峰执行肯定影响系统运行效率,那么规定只有下... 查看详情

基于rabbitmq实现分布式延时任务调度(代码片段)

...入数据库,使用定时去扫描,比对任务是否到期,到期则执行并设置任务状态为完成。这种做法在分布式环境下还需要对定时扫描做特殊处理(加分布式锁)避免任务被重复执行。然而使用RabbitMQ实现延时任务可以天然解决分布... 查看详情

redis延时任务看一篇成高手系列2

...sp;定时任务有明确的触发时间,延时任务没有定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执 查看详情

celery---一个懂得异步任务,延时任务,周期任务的芹菜(代码片段)

Celery是什么?celey是芹菜celery是基于Python实现的模块,用于执行异步延时周期任务的其结构组成是由  1.用户任务app  2.管道任务broker用于存储任务官方推荐redisrabbitMQ/backend用于存储任务执行结果的  3.员工workerCelery的简单示... 查看详情

订单30分钟未支付自动取消怎么实现?(代码片段)

...础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~Github地址:https://github.com/Tyson0314/Java-learning目录了解需求方案1:数据库轮... 查看详情

关于面试中异步与延时执行顺序的预期结果问题(代码片段)

理解同步与异步的概念(看第一个图)宏任务与微任务,简单说主线程上的最外层代码块就是宏任务(包括Promise和setTimeout),如果最外层代码块是异步任务,那么它内部的任务就是微任务宏任务与微任务相当于主线程和子线程的... 查看详情

延时任务实现方案总结(代码片段)

...时任务有明确的触发时间,延时任务没有定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执行周期定时任务一般执行的是批处理操作是多个任务,而延时任务一般是单个任务下面,我... 查看详情

springboot入门系列如何实现异步执行任务

前面介绍了SpringBoot如何整合定时任务,不清楚的朋友可以看看之前的文章:https://www.cnblogs.com/zhangweizhong/category/1657780.html。今天主要讲解SpringBoot中的另外一个任务:异步任务。所谓异步任务,其实就是异步执行程序,有些时候... 查看详情

celery时区设置问题源码探究

...在使用过程中出现过延时任务及周期任务到预定时间未能执行的情况。Google、百度了一些网友的分析及解决方案,大多认为是Celery时区设置导致的问题。然而这些解答大多类似,而且并不能解决我心中的疑惑,因此决定研究源码... 查看详情

juc并发编程共享模式之工具threadpoolexecutor--任务调度线程池定时任务/延时执行(timer的缺点)(代码片段)

...务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务。1.1.1正常情 查看详情