Java高并发专题之35、延迟队列 DelayQueue 详解

目录1. 前言在我的项目中有这样一个场景:页面链接是同一个,但是可以有多个子页面,不同的时间要展示不同子页面,类似一个页面排期功能。也许你们觉得要实现这个功能比较简单,实现过程为:获取所有子页面的生效时间,对每个生效时间点创建一个定时器,每个定时器执行内容为使用新的子页面进行渲染。对于单个或者少量页面这样做完全没有问题,但是在我的项目中每天都有上万个这样的页面需要进行排期。如果采用这种方...
摘要由CSDN通过智能技术生成

目录

1. 前言

在我的项目中有这样一个场景:页面链接是同一个,但是可以有多个子页面,不同的时间要展示不同子页面,类似一个页面排期功能。也许你们觉得要实现这个功能比较简单,实现过程为:获取所有子页面的生效时间,对每个生效时间点创建一个定时器,每个定时器执行内容为使用新的子页面进行渲染。对于单个或者少量页面这样做完全没有问题,但是在我的项目中每天都有上万个这样的页面需要进行排期。如果采用这种方式,势必会创建上万个定时器放到jvm内存,这显然是不科学的。

我们的做法是把 每个子页面排期看成是一个任务放到任务表,任务执行时间即为子页面的开始时间,再通过一个分布式任务调度器,每次获取将来5分钟内即将执行的任务列表。把这些任务放到一个DelayQueue中,每个子页面开始时间到达时,从DelayQueue中取出,执行页面渲染,这时用户浏览到的页面就是最新的内容。

这里通过分布式任务调度器,可以把任务均分到各个服务器上,并且每次获取任务是指取将来5分钟内即将执行的任务列表,这个列表一般不会太多,可以直接放到队列中。当然如果很多也没关系,可以指定获取最大任务条数。通过上述处理可以控制放入DelayQueue的任务数,减少不必要的内存消耗。如下图:假如5分钟内即将执行的任务列表有9个,通过分布式调度分配到每台机器上的任务数即为3个:

关于分布式任务调度,可以使用淘宝的tbschedule等类似的框架支持,也可以自己实现。这里不详细讲解分布式任务调度怎么实现,今天的主角是DelayQueue—延迟队列。

现在我们已经获取到每台机器上的即将执行的任务列表,接下来就是把这些任务放到DelayQueue,通过其take方法获取到期的任务定时执行。

2. DelayQueue基本特征

DelayQueue延迟队列同时具备:无界队列、阻塞队列、优先队列的特征。分别来看下:

无界队列:通过调用DelayQueue的offer方法(或add方法),把待执行的任务对象放入队列,该方法是非阻塞的。这个队列是无界队列,内存足够的情况下,理论上存放的任务对象数是无限的。

阻塞队列:DelayQueue实现了BlockingQueue接口,是一个阻塞队列。但该队列只是在取对象时阻塞,对应两个方法:1、take()方法,获取并移除队列头的对象,如果时间还未到,就阻塞等待。2、poll(long timeout, TimeUnit unit) 方法,阻塞时间长度为timeout,然后获取并移除队列头的对象,如果对象延迟时间还未到,就返回null。

优先队列:DelayQueue的一个重要的成员是一个优先队列PriorityQueue,PriorityQueue内部是一个二叉小顶堆实现,其特点就是头部元素对应的权值是队列中最小的,也就是通过poll()方法获取到的对象是最优先的。

3. Delayed接口

DelayQueue延迟队列中存放的对象,必须是实现Delayed接口的类对象。Delayed接口,是Comparable的子类:

  1. public interface Delayed extends Comparable<Delayed>

所有要实现Delayed接口必须重写其getDelay、compareTo方法。

看一个实现例子:

public class TaskInfo implements Delayed {  

    //任务id  
    private int id;  

    //业务类型  
    private int type;  

    //业务数据  
    private String data;  

    //执行时间  
    private long excuteTime;  

    public TaskInfo(int id, int type, String data, long excuteTime) {  
        this.id = id;  
        this.type = type;  
        this.data = data;  
        this.excuteTime = TimeUnit.NANOSECONDS.convert(excuteTime, TimeUnit.MILLISECONDS)+System.nanoTime();  
    }  

    public int getId() {  
        return id;  
    }  

    public void setId(int id) {  
        this.id = id;  
    }  

    public int getType() {  
        return type;  
    }  

    public void setType(int type) {  
        this.type = type;  
    }  

    public String getData() {  
        return data;  
    }  

    public void setData(String data) {  
        this.data = data;  
    }  

    public long getExcuteTime() {  
        return excuteTime;  
    }  

    public void setExcuteTime(long excuteTime) {  
        this.excuteTime = excuteTime;  
    }  

    @Override  
    public long getDelay(TimeUnit unit) {  
        return unit.convert(this.excuteTime- System.nanoTime() , TimeUnit.NANOSECONDS);  
    }  

    @Override  
    public int compareTo(Delayed o) {  
        TaskInfo msg = (
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值