DelayQueue队列

本秂侑毒 提交于 2020-01-07 16:04:30

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

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

我们可以将DelayQueue运用在以下应用场景:

缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。

定时任务调度:使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,从比如TimerQueue就是使用DelayQueue实现的。

——以上摘自Java并发编程的艺术

下面是自已撸的一个小栗子

1、用于执行通知任务的队列元素

package com.dreyer.concurrent;

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

/**
 * @description 通知延迟队列
 * @author: Dreyer
 * @date: 16/4/27 下午2:40
 */
public class NotifyTask implements Runnable, Delayed {
    // 通知名称
    private String notifyTaskName;
    // 执行时间
    private long executeTime;

    public NotifyTask(String notifyTaskName, long executeTime) {
        this.notifyTaskName = notifyTaskName;
        this.executeTime = executeTime;
    }

    /**
     * 设置延迟时间
     * @param unit
     * @return
     */
    public long getDelay(TimeUnit unit) {
        return unit.convert(executeTime - System.currentTimeMillis(), unit.MILLISECONDS);
    }

    /**
     * 用来指定元素的顺序,让延时时间最长的放在队列的末尾
     * @param o
     * @return
     */
    public int compareTo(Delayed o) {
        NotifyTask notifyTask = (NotifyTask) o;
        return executeTime > notifyTask.executeTime ? 1 : (executeTime < notifyTask.executeTime ? -1 : 0);
    }

    public void run() {
        System.out.println("当前时间毫秒数:" + System.currentTimeMillis() + "," + this.toString() + "正在执行...");
    }

    @Override
    public String toString() {
        return "NotifyTask{" +
                "notifyTaskName='" + notifyTaskName + '\'' +
                ", executeTime=" + executeTime +
                '}';
    }
}

2、测试类

package com.dreyer.concurrent;

import java.util.Random;
import java.util.concurrent.DelayQueue;

/**
 * @description 测试类
 * @author: Dreyer
 * @date: 16/4/27 下午3:56
 */
public class NotifyTaskTest {
    /**
     * 通知任务存放的延迟队列
     */
    public static DelayQueue<NotifyTask> tasks = new DelayQueue<NotifyTask>();


    public static void main(String[] args) {
        Random random = new Random();
        for (int i = 0; i < 5; i++) {
            // 随机产生一个秒数
            int seconds = random.nextInt(5) + 1;
            NotifyTask notifyTask = new NotifyTask("任务" + i,System.currentTimeMillis() + (seconds * 1000));
            tasks.put(notifyTask);
        }
        long start = System.currentTimeMillis();
        while (true) {
            NotifyTask notifyTask = tasks.poll();

            if (notifyTask != null) {
                notifyTask.run();
            }
            // 如果队列中的元素全部被取完,则跳出循环
            if (tasks.size() == 0) {
                break;
            }
            System.out.println("is running......");
        }
        System.out.println("耗时:" + (System.currentTimeMillis() - start) / 1000 + "ms");

    }

}

3、执行结果(部分)

......

is running......

is running......

is running......

is running......

is running......

is running......

is running......

is running......

is running......

is running......

当前时间毫秒数:1461762721192,NotifyTask{notifyTaskName='任务0', executeTime=1461762721192}正在执行...

is running......

当前时间毫秒数:1461762721192,NotifyTask{notifyTaskName='任务1', executeTime=1461762721192}正在执行...

耗时:5ms

从结果中,我们可以看出NotifyTask的executeTime是设置在什么时候执行,那它的执行时间也会是那个时候


注意点:

栗子中的构造函数设置的延迟时间参数executeTime的单位是毫秒,getDelay()方法可以指定任意单位(栗子中指定的是毫秒),建议不要使用值过大的单位,比如秒 / 分,如果getDelay()方法返回的是一个负数,那队列中能立即获取到元素。

关于DelayQueue在公司项目里面的应用场景主要是:

订单支付成功或者失败后要给商户发送相应的通知,针对同一条通知记录,如果是第一次发,则需要等待的时间是0分钟,第二次发则需要等待1分钟,第三次发则需要等待3分钟,即发送次数每+1,则需要等待的时长也要相应的增加,那使用DelayQueue就能很好的实现这个功能了。








标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!