Home 成长之路 Leetcode题解 延时队列

延时队列

深夜,凝芳殿中。

"奴婢参见玉妃,玉妃娘娘万福金安",林婉拜跪着说道。
身前不远处,坐着一位天生媚骨,身材极好的女子,此刻她正慵懒的抱着一只波斯猫,轻轻的抚摸着。

"本宫听闻浣衣监新来了个婢子,是工部尚书林任之的侄女,年纪不大,样貌倒是极美,还未出阁就引得京城王李两家的公子争相提亲",
玉妃将怀里的波斯猫放下,站起身来,端起一盏灯,缓缓向林婉走来。

虽然被发配到浣衣监才几日光景,林婉却也听其他人说起过,在后宫行事千万不要得罪玉妃,当日她好奇追问这是为何,众人却死活不敢说下去,
此刻已是亥时,无论是浣衣监还是凝芳殿都应该早早休息了才对,可偏偏玉妃差人唤自己进殿,过来的一路上,林婉思索了一番,实在想不到自己何时招惹了这尊大佛,只得在心里暗暗祈祷不要出什么事才好。

"本宫倒要看看,这大名鼎鼎的林婉,是不是生的似仙女一般夺人心魄"玉妃轻轻笑了几声,慕然间已然走到了林婉的面前。
"抬起头来"玉妃的声音突然从林婉耳边响起,既不温柔也不冰冷,彷佛没有感情的机器。

林婉乖乖的把头抬起,这才看清玉妃的模样,烛火映照之下,玉妃容色晶莹如玉,如新月生晕,花树堆雪,明眸皓齿,好似九重宫阙的谪仙子一般,难怪在这后宫能如此受宠!
玉妃也在看着她,二八年纪,生得浑然天成的绝美脸庞,朱唇皓齿,亦是美得不可方艳。

两人微微出神,还是林婉先反应过来,连忙低下头,生怕冒犯了玉妃。
玉妃出身高贵,自然眼界不低,何况在这后宫中最不缺的就是各样各式的美女,饶是如此她也不得不承认眼前婢女的容貌称得上祸国殃民。

玉妃深深的瞥了林婉一眼,随即说道,"本宫今夜唤你前来,就是想见识下你的样貌,现在看来,也不过如此,下去吧"。
林婉闻言如释重负,立马起身,顾不得那么许多退出了凝芳殿。

回到浣衣监,林婉还在思索着刚刚发生的事情,她觉得玉妃深夜唤她进宫一定不是看她的容貌这般简单,一夜无眠。
林婉不知道的是,玉妃今夜也失眠了...
--------------------------------------------- 我是分割线 --------------------------------------------------------------------------------------------------------------
次日叫醒林婉的不是公公,而是凝芳殿的婢子秋莹,辰时未至她就到了浣衣监,在门口等了半个时辰,
开门的婢子一来,秋莹就急匆匆的让她带自己去找林婉。
吱呀一声,林婉的房门被推开,此时屋内还是




 在这里插入图片描述

背景:搭建业务异常的监控告警系统,要求支持两种告警类型

一个是首次告警,好理解,接收到上报的异常就告警

另一个则是统计告警:统计在首次告警后五分钟内该异常出现的总次数

 

给你五分钟,你会想到什么方式实现统计告警?我想到了定时任务…

其实这是一个典型的延时队列的使用场景

什么是延时队列?

延时队列,首先,它是一种队列,队列意味着内部的元素是有序的,元素出队和入队是有方向性的,元素从一端进入,从另一端取出。
其次,延时队列,最重要的特性就体现在它的延时属性上,跟普通的队列不一样的是,普通队列中的元素总是等着希望被早点取出处理,而延时队列中的元素则是希望被在指定时间得到取出和处理,所以延时队列中的元素是都是带时间属性的,通常来说是需要被处理的消息或者任务。
简单来说,延时队列就是用来存放需要在指定时间被处理的元素的队列。

延时队列的应用场景

  • 在淘宝、京东等购物平台上下单,超过一定时间未付款,订单会自动取消。
  • 打车的时候,在规定时间没有车主接单,平台会取消你的单并提醒你暂时没有车主接单。
  • 点外卖的时候,如果商家在10分钟还没接单,就会自动取消订单。
  • 收快递的时候,如果我们没有点确认收货,在一段时间后程序会自动完成订单。
  • 在平台完成订单后,如果我们没有在规定时间评论商品,会自动默认买家不评论。

 

延时队列的实现方式

1、DelayQueue 延时队列

JDK 中提供了一组实现延迟队列的API,位于Java.util.concurrent包下DelayQueue

DelayQueue是一个BlockingQueue(无界阻塞)队列,它本质就是封装了一个PriorityQueue(优先队列),PriorityQueue内部使用完全二叉堆(不知道的自行了解哈)来实现队列元素排序,我们在向DelayQueue队列中添加元素时,会给元素一个Delay(延迟时间)作为排序条件,队列中最小的元素会优先放在队首。队列中的元素只有到了Delay时间才允许从队列中取出。队列中可以放基本数据类型或自定义实体类,在存放基本数据类型时,优先队列中元素默认升序排列,自定义实体类就需要我们根据类属性值比较计算了。

先简单实现一下看看效果,添加三个order入队DelayQueue,分别设置订单在当前时间的5秒10秒15秒后取消。

在这里插入图片描述

要实现DelayQueue延时队列,队中元素要implements Delayed 接口,这个接口里只有一个getDelay方法,用于设置延期时间。Order类中compareTo方法负责对队列中的元素进行排序。

public class Order implements Delayed {
    /**
     * 延迟时间
     */
    @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private long time;
    String name;
    
    public Order(String name, long time, TimeUnit unit) {
        this.name = name;
        this.time = System.currentTimeMillis() + (time > 0 ? unit.toMillis(time) : 0);
    }
    
    @Override
    public long getDelay(TimeUnit unit) {
        return time - System.currentTimeMillis();
    }
    @Override
    public int compareTo(Delayed o) {
        Order Order = (Order) o;
        long diff = this.time - Order.time;
        if (diff <= 0) {
            return -1;
        } else {
            return 1;
        }
    }
}

DelayQueueput方法是线程安全的,因为put方法内部使用了ReentrantLock锁进行线程同步。DelayQueue还提供了两种出队的方法 poll()take()poll() 为非阻塞获取,没有到期的元素直接返回null;take() 阻塞方式获取,没有到期的元素线程将会等待。

public class DelayQueueDemo {

    public static void main(String[] args) throws InterruptedException {
        Order Order1 = new Order("Order1", 5, TimeUnit.SECONDS);
        Order Order2 = new Order("Order2", 10, TimeUnit.SECONDS);
        Order Order3 = new Order("Order3", 15, TimeUnit.SECONDS);
        DelayQueue<Order> delayQueue = new DelayQueue<>();
        delayQueue.put(Order1);
        delayQueue.put(Order2);
        delayQueue.put(Order3);

        System.out.println("订单延迟队列开始时间:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        while (delayQueue.size() != 0) {
            /**
             * 取队列头部元素是否过期
             */
            Order task = delayQueue.poll();
            if (task != null) {
                System.out.format("订单:{%s}被取消, 取消时间:{%s}\n", task.name, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            }
            Thread.sleep(1000);
        }
    }
}

上边只是简单的实现入队与出队的操作,实际开发中会有专门的线程,负责消息的入队与消费。

执行后看到结果如下,Order1Order2Order3 分别在 5秒10秒15秒后被执行,至此就用DelayQueue实现了延时队列。

订单延迟队列开始时间:2020-05-06 14:59:09
订单:{Order1}被取消, 取消时间:{2020-05-06 14:59:14}
订单:{Order2}被取消, 取消时间:{2020-05-06 14:59:19}
订单:{Order3}被取消, 取消时间:{2020-05-06 14:59:24

 

 

SIMILAR ARTICLES

0 8

0 9

发表评论

发表评论