Java延迟队列——DelayQueue

简介: 一、DelayQueue的定义

一、DelayQueue的定义

public class DelayQueue<E extends Delayed> extends AbstractQueue<E> implements BlockingQueue<E>

DelayQueue是一个无界的BlockingQueue,是线程安全的(无界指的是队列的元素数量不存在上限,队列的容量会随着元素数量的增加而扩容,阻塞队列指的是当队列内元素数量为0的时候,试图从队列内获取元素的线程将被阻塞或者抛出异常)


以上是阻塞队列的特点,而延迟队列还拥有自己如下的特点:


DelayQueue中存入的必须是实现了Delayed接口的对象(Delayed定义了一个getDelay的方法,用来判断排序后的元素是否可以从Queue中取出,并且Delayed接口还继承了Comparable用于排序),插入Queue中的数据根据compareTo方法进行排序(DelayQueue的底层存储是一个PriorityQueue,PriorityQueue是一个可排序的Queue,其中的元素必须实现Comparable接口的compareTo方法),并通过getDelay方法返回的时间确定元素是否可以出队,只有小于等于0的元素(即延迟到期的元素)才能够被取出


延迟队列不接收null元素

二、DelayQueue的作用

延迟队列的作用显然就是用于执行延时任务,如:

淘宝订单业务:下单之后如果三十分钟之内没有付款就自动取消订单。

饿了吗订餐通知:下单成功后60s之后给用户发送短信通知。

关闭空闲连接。服务器中,有很多客户端的连接,空闲一段时间之后需要关闭之。

缓存。缓存中的对象,超过了空闲时间,需要从缓存中移出。

任务超时处理。在网络协议滑动窗口请求应答式交互时,处理超时未响应的请求等。

三、DelayQueue的实现

三、DelayQueue的实现

定义延迟队列元素

public class OrderDelayTask implements Delayed {
    private Long orderId;
    private long delayTime;
    public OrderDelayTask(Long orderId, long delayTime) {
        this.orderId = orderId;
        // 延迟时间加当前时间
        this.delayTime = System.currentTimeMillis() + delayTime;
    }
    // 获取任务剩余时间
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(delayTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }
    @Override
    public int compareTo(Delayed o) {
        return Long.compare(delayTime, ((OrderDelayTask) o).delayTime);
    }
}

定义延迟队列并交付容器管理

@Bean("orderDelayQueue")
public DelayQueue<OrderDelayTask> orderDelayQueue(){
    return new DelayQueue<OrderDelayTask>();
}

使用延迟队列

@Autowired
private DelayQueue<OrderDelayObject> orderDelayQueue;
//发起订单下单的时候将订单演示对象放入orderDelayQueue
orderDelayQueue.add(
        new OrderDelayTask(
                "123456", // 订单id
                30 * 60 * 1000, // 延迟时间:30分钟
        )
);

开启线程处理延迟任务

@Component
public class DelayTaskRunner<OrderDelayTask> implements InitializingBean {
    @Autowired
    private DelayQueue<OrderDelayTask> orderDelayQueue;
    @Override
    public void afterPropertiesSet() throws Exception {
      new Thread(() -> {
            try {
                while(true) {
                    OrderDelayTask task = orderDelayQueue.take();
                    // 当队列为null的时候,poll()方法会直接返回null, 不会抛出异常,但是take()方法会一直等待,因此会抛出一个InterruptedException类型的异常。(当阻塞方法收到中断请求的时候就会抛出InterruptedException异常)
                    Long orderId = task.getOrderId();
                    // 执行业务
                }
            } catch (InterruptedException e) { 
                // 因为是重写Runnable接口的run方法,子类抛出的异常要小于等于父类的异常。而在Runnable中run方法是没有抛异常的。所以此时是不能抛出InterruptedException异常。如果此时你只是记录日志的话,那么就是一个不负责任的做法,因为在捕获InterruptedException异常的时候自动的将是否请求中断标志置为了false。在捕获了InterruptedException异常之后,如果你什么也不想做,那么就将标志重新置为true,以便栈中更高层的代码能知道中断,并且对中断作出响应。
              Thread.currentThread().interrupt();
            }
        }).start();
    }
}

四、DelayQueue实现延时任务的优缺点

使用DelayQueue实现延时任务非常简单,而且简便,全部都是标准的JDK代码实现,不用引入第三方依赖(不依赖redis实现、消息队列实现等),非常的轻量级。

它的缺点就是所有的操作都是基于应用内存的,一旦出现应用单点故障,可能会造成延时任务数据的丢失。如果订单并发量非常大,因为DelayQueue是无界的,订单量越大,队列内的对象就越多,可能造成OOM的风险。所以使用DelayQueue实现延时任务,只适用于任务量较小的情况。

相关文章
|
1月前
|
SQL 监控 Java
Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面
本文探讨了Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,以实现高效稳定的数据库访问。示例代码展示了如何使用HikariCP连接池。
21 2
|
2月前
|
存储 安全 Java
【用Java学习数据结构系列】探索栈和队列的无尽秘密
【用Java学习数据结构系列】探索栈和队列的无尽秘密
37 2
|
3月前
|
Java API 容器
JAVA并发编程系列(10)Condition条件队列-并发协作者
本文通过一线大厂面试真题,模拟消费者-生产者的场景,通过简洁的代码演示,帮助读者快速理解并复用。文章还详细解释了Condition与Object.wait()、notify()的区别,并探讨了Condition的核心原理及其实现机制。
|
2月前
|
存储 算法 Java
【用Java学习数据结构系列】用堆实现优先级队列
【用Java学习数据结构系列】用堆实现优先级队列
38 0
|
4月前
|
Java
java中的队列
这篇文章通过Java代码示例介绍了使用数组实现队列操作,包括队列的初始化、入队、出队、判断队列满和空以及遍历队列的方法。
java中的队列
|
5月前
|
存储 算法 Java
Java ZGC 深度剖析及其在构建低延迟流系统中的实践心得
AutoMQ 基于 ZGC 进行了一系列调优,以获得更低的延迟。在本文中,我们将详细介绍 ZGC 的工作原理,以及如何通过调整和优化 ZGC 的配置来实现更低的延迟,从而提高 Java 应用程序的性能和响应能力。
71 2
Java ZGC 深度剖析及其在构建低延迟流系统中的实践心得
|
5月前
|
设计模式 安全 Java
Java面试题:请解释Java中的线程池以及为什么要使用线程池?请解释Java中的内存模型以及如何避免内存泄漏?请解释Java中的并发工具包以及如何实现一个简单的线程安全队列?
Java面试题:请解释Java中的线程池以及为什么要使用线程池?请解释Java中的内存模型以及如何避免内存泄漏?请解释Java中的并发工具包以及如何实现一个简单的线程安全队列?
49 1
|
5月前
|
存储 算法 Java
Java ZGC 深度剖析及其在构建低延迟流系统中的实践心得
Java ZGC 深度剖析及其在构建低延迟流系统中的实践心得
35 0
Java ZGC 深度剖析及其在构建低延迟流系统中的实践心得
|
6月前
|
Java 开发者
揭秘!LinkedList是如何华丽变身成为Java队列之王的?
【6月更文挑战第18天】Java的`LinkedList`既是列表也是队列之星,实现`Queue`接口,支持FIFO操作。其内部的双向链表结构确保了添加/移除元素的高效性(O(1)),适合作为队列使用。它线程不安全,但可通过同步包装用于多线程环境。此外,`LinkedList`还能灵活变身栈或双端队列,提供多种数据结构功能。
69 11
|
5月前
|
缓存 开发者 Java
java枚举消除冗余代码问题之需要延迟注册枚举到缓存问题如何解决
java枚举消除冗余代码问题之需要延迟注册枚举到缓存问题如何解决