Java并发新构件之DelayQueue

    DelayQueue主要用于放置实现了Delay接口的对象,其中的对象只能在其时刻到期时才能从队列中取走。这种队列是有序的,即队头的延迟到期时间最短。如果没有任何延迟到期,那就不会有任何头元素,并且poll()将返回null(正因为这样,你不能将null放置到这种队列中)

    下面是一个示例,其中的Delayed对象自身就是任务,而DelayedTaskConsumer将最“紧急”的任务从队列中取出来,然后运行它:

1import java.util.ArrayList; 2import java.util.List; 3import java.util.Random; 4import java.util.concurrent.DelayQueue; 5import java.util.concurrent.Delayed; 6import java.util.concurrent.ExecutorService; 7import java.util.concurrent.Executors; 8import java.util.concurrent.TimeUnit; 9import static java.util.concurrent.TimeUnit.*; 10 11class DelayedTask implements Runnable, Delayed { 12 13    private static int counter = 0; 14    protected static List<DelayedTask> sequence = new ArrayList<>(); 15    private final int id = counter++; 16    private final int delayTime; 17    private final long triggerTime; 18    public DelayedTask(int delayInMillis) { 19        delayTime = delayInMillis; 20        triggerTime = System.nanoTime() + NANOSECONDS.convert(delayTime, MILLISECONDS); 21        sequence.add(this); 22    } 23     24    @Override 25    public int compareTo(Delayed o) { 26        DelayedTask that = (DelayedTask)o; 27        if (triggerTime < that.triggerTime) return -1; 28        if (triggerTime > that.triggerTime) return 1; 29        return 0; 30    } 31 32    /** 33     * 剩余的延迟时间 34     */ 35    @Override 36    public long getDelay(TimeUnit unit) { 37        return unit.convert(triggerTime - System.nanoTime(), NANOSECONDS); 38    } 39 40    @Override 41    public void run() { 42        System.out.println(this + " "); 43    } 44     45    @Override 46    public String toString() { 47        return String.format("[%1$-4d]", delayTime) + " Task " + id; 48    } 49     50    public static class EndSentinel extends DelayedTask { 51        private ExecutorService exec; 52        public EndSentinel(int delay, ExecutorService exec) { 53            super(delay); 54            this.exec = exec; 55        } 56        @Override 57        public void run() { 58            System.out.println(this + " calling shutDownNow()"); 59            exec.shutdownNow(); 60        } 61    } 62} 63 64class DelayedTaskConsumer implements Runnable { 65    private DelayQueue<DelayedTask> tasks; 66    public DelayedTaskConsumer(DelayQueue<DelayedTask> tasks) { 67        this.tasks = tasks; 68    } 69    @Override 70    public void run() { 71        try { 72            while(!Thread.interrupted()) { 73                tasks.take().run();//run tasks with current thread. 74            } 75        } catch (InterruptedException e) { 76            // TODO: handle exception 77        } 78        System.out.println("Finished DelaytedTaskConsumer."); 79    } 80} 81 82 83public class DelayQueueDemo { 84    public static void main(String[] args) { 85        int maxDelayTime = 5000;//milliseconds 86        Random random = new Random(47); 87        ExecutorService exec = Executors.newCachedThreadPool(); 88        DelayQueue<DelayedTask> queue = new DelayQueue<>(); 89        //填充10个休眠时间随机的任务 90        for (int i = 0; i < 10; i++) { 91            queue.put(new DelayedTask(random.nextInt(maxDelayTime))); 92        } 93        //结束的哨兵任务,延迟时间最长。这样当执行到这个任务的时候,就是最后一个任务了,可以关闭exec 94        queue.add(new DelayedTask.EndSentinel(maxDelayTime, exec)); 95        exec.execute(new DelayedTaskConsumer(queue)); 96    } 97}

执行结果:

1[200 ] Task 7  2[429 ] Task 5  3[555 ] Task 1  4[961 ] Task 4  5[1207] Task 9  6[1693] Task 2  7[1861] Task 3  8[4258] Task 0  9[4522] Task 8  10[4868] Task 6  11[5000] Task 10 calling shutDownNow() 12Finished DelaytedTaskConsumer.

    DelayedTask包含一个称为sequence的List<DelayedTask>,它保存了在任务被创建的顺序,因此我们可以看到排序是按照实际发生的顺序执行的(即到期时间短的先出队列)。

    Delayed接口有一个方法名为getDelay(),它可以用来告知延迟到期还有多长时间,或者延迟在多长时间之前已经到期。这个方法将强制我们去使用TimeUnit类,因为这就是参数类型。这会产生一个非常方便的类,因为你可以很容易地转换单位而无需做任何声明。例如,delayTime的值是以毫秒为单位的,但是System.nanoTime()产生的时间则是以纳秒为单位的。你可以转换delayTime的值,方法是声明它的单位以及你希望以什么单位来表示,就像下面这样:

NANOSECONDS.convert(delayTime, MILLISECONDS);

    为了排序,Delayed接口还继承了Comparable接口,因此必须实现compareTo()方法,使其可以产生合理的比较。toString()则提供了输出格式化,而嵌套的EndSentinel类提供了一种关闭所有事物的途径,具体做法是将其放置为队列的最后一个元素。

    注意,因为DelayedTaskConsumer自身是一个任务,所以它有自己的Thread,它可以使用这个线程来运行从队列中获取的所有任务。由于任务是按照队列优先级的顺序来执行的,因此在本例中不需要启动任何单独的线程来运行DelayedTask。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Java日期时间API系列31

  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前