Title

DelayQueue 延迟队列使用

一、DelayQueue是什么

  DelayQueue是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。

二、DelayQueue能做什么

 1. 淘宝订单业务:下单之后如果三十分钟之内没有付款就自动取消订单。
 2. 饿了吗订餐通知:下单成功后60s之后给用户发送短信通知。
 3.关闭空闲连接。服务器中,有很多客户端的连接,空闲一段时间之后需要关闭之。
 4.缓存。缓存中的对象,超过了空闲时间,需要从缓存中移出。
 5.任务超时处理。在网络协议滑动窗口请求应答式交互时,处理超时未响应的请求等。

三、实例展示

定义元素类,作为队列的元素

 DelayQueue只能添加(offer/put/add)实现了Delayed接口的对象,意思是说我们不能想往DelayQueue里添加什么就添加什么,不能添加int、也不能添加String进去,必须添加我们自己的实现了Delayed接口的类的对象,来代码:

/**
 *  compareTo 方法必须提供与 getDelay 方法一致的排序
 */
class MyDelayedTask implements Delayed{

    private String name ;
    private long start = System.currentTimeMillis();
    private long time ;

    public MyDelayedTask(String name,long time) {
        this.name = name;
        this.time = time;
    }

    /**
     * 需要实现的接口,获得延迟时间   用过期时间-当前时间
     * @param unit
     * @return
     */
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert((start+time) - System.currentTimeMillis(),TimeUnit.MILLISECONDS);
    }

    /**
     * 用于延迟队列内部比较排序   当前时间的延迟时间 - 比较对象的延迟时间
     * @param o
     * @return
     */
    @Override
    public int compareTo(Delayed o) {
        MyDelayedTask o1 = (MyDelayedTask) o;
        return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
    }

    @Override
    public String toString() {
        return "MyDelayedTask{" +
                "name='" + name + '\'' +
                ", time=" + time +
                '}';
    }
}

其中,compareTo 方法 getDelay 方法 就是Delayed接口的方法,我们必须实现,而且按照JAVASE文档,compareTo 方法必须提供与 getDelay 方法一致的排序,也就是说compareTo方法里可以按照getDelay方法的返回值大小排序,即在compareTo方法里比较getDelay方法返回值大小

写main方法测试

定义一个DelayQueue,添加几个元素,while循环获取元素

private static DelayQueue delayQueue  = new DelayQueue();
    public static void main(String[] args) throws InterruptedException {

        new Thread(new Runnable() {
            @Override
            public void run() {

                delayQueue.offer(new MyDelayedTask("task1",10000));
                delayQueue.offer(new MyDelayedTask("task2",3900));
                delayQueue.offer(new MyDelayedTask("task3",1900));
                delayQueue.offer(new MyDelayedTask("task4",5900));
                delayQueue.offer(new MyDelayedTask("task5",6900));
                delayQueue.offer(new MyDelayedTask("task6",7900));
                delayQueue.offer(new MyDelayedTask("task7",4900));

            }
        }).start();

        while (true) {
            Delayed take = delayQueue.take();
            System.out.println(take);
        }
    }

执行结果

DelayQueue属于排序队列,它的特殊之处在于队列的元素必须实现Delayed接口,该接口需要实现compareTo和getDelay方法。

static class Task implements Delayed{
        @Override
                //比较延时,队列里元素的排序依据
        public int compareTo(Delayed o) {
            return 0;
        }
        
        @Override
                //获取剩余时间
        public long getDelay(TimeUnit unit) {
            return 0;
        }
    }

元素进入队列后,先进行排序,然后,只有getDelay也就是剩余时间为0的时候,该元素才有资格被消费者从队列中取出来,所以构造函数一般都有一个时间传入。

另一个例子:

import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * 定时创建
 */
@Slf4j
@Component
public class TestJob implements Runnable {
    @Resource
    private OrderService orderService;
	
    private static final DelayQueue<DelayElement> DELAY_QUEUE = new DelayQueue<>();

    /**
     * 定时扫描
     */
    @XxlJob("testJobHandler")
    public void autoGenerateDoJob() {
        log.info("开始执行扫描");
		DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
		// 获取需要添加延时处理的数据
        List<Order> orderList = orderService.getOrderList();
        for (Order one : orderList) {
		    // 获取执行时间
            LocalDateTime time = LocalDateTime.parse(one.getNextTime(), format);
            // 获取延迟时间
            Duration duration = Duration.between(LocalDateTime.now(), time);
            DELAY_QUEUE.put(new PlanJob.DelayElement(duration.toMillis(), order));
            // 更新下次执行时间
            String nextTime = "下次执行时间";
            Order update = new Order();
            update.setId(order.getId());
            update.setNextTime(nextTime);
            orderService.updateById(update);
        }
        log.info("执行扫描结束...");
    }

    @PostConstruct
    public void init() {
        new Thread(this).start();
    }

    @Override
    public void run() {
        synchronized (this) {
            while (true) {
                // 执行延迟任务
                try {
                    // 获取延时数据,执行操作
                    DelayElement take = DELAY_QUEUE.take();
                    Order order = take.getOrder();
					
                    // todo 执行操作

                } catch (Exception e) {
                    log.info("任务失败", e.getMessage(), e);
                }

            }
        }
    }

    @Data
    static class DelayElement implements Delayed {
        long delayTime = System.currentTimeMillis();

        private Order order;

        public DelayElement(long delayTime, Order order) {
            this.delayTime = this.delayTime + delayTime;
            this.order = order;
        }

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

        @Override
        public int compareTo(@NotNull Delayed o) {
            return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
        }
    }
}

原文章地址:
https://www.cnblogs.com/myseries/p/10944211.html

posted @ 2024-08-16 17:46  快乐小洋人  阅读(165)  评论(0编辑  收藏  举报