利用java写的java.util.concurrent.Delayed 类 配合线程实现延迟队列处理方案

82 阅读2分钟

直接分享代码

package com.xxx.init.delayJob;

import lombok.Data;

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

/**
 * User:Json
 * Date: 2024/5/15
 * java 内置的延迟队列
 * 服务器重启后,数据全部消失,怕宕机
 * 他是无界队列 可以一直放 有可能导致内存满的问题
 * 所以不宜处理 任务比较多的业务
 * 用来处理 任务少并不是很重要的数据
 * 如果数据放的太快 有可能 顺序也会错 
 **/
@Data
public class DelayTask<T>  implements Delayed {
    final private T data;
    final private long expire;

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

    //返回任务的剩余延迟时间,用于决定任务何时可以从队列中取出
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.expire - System.currentTimeMillis(), unit);
    }


    // 比较两个延迟任务的优先级,决定它们在队列中的顺序
    //如果delta等于0,说明两个任务的延迟时间相同,返回0。
    //如果delta小于0,说明当前任务的延迟时间小于另一个任务,返回-1,表示当前任务优先级更高。
    //如果delta大于0,说明当前任务的延迟时间大于另一个任务,返回1,表示当前任务优先级更低。
    @Override
    public int compareTo(Delayed o) {
        long delay1 = getDelay(TimeUnit.NANOSECONDS);
        long delay2 = o.getDelay(TimeUnit.NANOSECONDS);
        return Long.compare(delay1, delay2);
    }
}

package com.xxx.iotjava.utils;

import com.xxx.init.delayJob.DelayTask;
import com.xxx.iotjava.entities.SkeAirCdRealtimeData;
import com.xxx.iotjava.job.AicCdJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.time.LocalDateTime;
import java.util.concurrent.DelayQueue;

/**
 * User:Json
 * Date: 2024/5/15
 * java 内置的延迟队列
 * 服务器重启后,数据全部消失,怕宕机
 * 他是无界队列 可以一直放 有可能导致内存满的问题
 * 所以不宜处理 任务比较多的业务
 * 用来处理 任务少并不是很重要的数据
 **/
@Component
@Slf4j
@Order(3)
public class DelayQueueUtil<T> implements CommandLineRunner {
    private final DelayQueue<DelayTask<T>> delayQueue = new DelayQueue<>();
    private volatile boolean running = true;

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    @Autowired
    AicCdJob aicCdJob;

    /**
     * 加入到延时队列中
     * @param task
     */
    public void put(DelayTask<T> task) {
       // log.info("加入延时任务时间:"+LocalDateTime.now()+",加入延时任务:"+task);
        delayQueue.put(task);
    }

    /**
     * 取消延时任务
     * @param task
     * @return
     */
    public boolean remove(DelayTask<T> task) {
        //log.info("取消延时任务时间:"+LocalDateTime.now()+",取消延时任务:{}", task);
        return delayQueue.remove(task);
    }

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

    //初始化延迟线程
    @Override
    public void run(String... args) {
        //log.info("初始化延时队列");
        taskExecutor.execute(this::executeThread);
    }

    /**
     * 延时任务执行线程
     */
    public void executeThread() {
        while (running) {
            try {
                DelayTask<T> task = delayQueue.take();
                processTask(task);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }

    /**
     * 内部执行延时任务
     * @param task
     */
    public void processTask(DelayTask<T> task) {
      //  log.info("执行延时任务时间:"+LocalDateTime.now()+"【参数】:"+task);
        if(task.getData() instanceof SkeAirCdRealtimeData){
            aicCdJob.aicCdRealTimeDatAll();
        }else if(task.getData() instanceof String){
            System.out.println(task.getData());
        }
        // 根据task中的data自定义数据来处理相关逻辑,例如 if (task.getData() instanceof XXX) {}
    }

    /**
     * 停止延时队列
     */
    public void stop() {
        running = false;
        Thread.currentThread().interrupt();
        taskExecutor.shutdown();
    }

    @PreDestroy
    public void onDestroy() {
        stop();
    }

}




调用

    @Autowired
    DelayQueueUtil delayQueueUtil;

    @GetMapping("index18")
    @ApiOperation(value = "测试延迟任务")
    public R index18(){
        for (int i=0;i<=20;i++){
            delayQueueUtil.put(new DelayTask<>("第"+i+"次", 2));
        }

        //delayQueueUtil.put(new DelayTask<>(new SkeAirCdRealtimeData(), 1));
        return R.success();
    }