实现延时任务有很多的方法,网上关于延时任务的实现的文章已经不少了。比如:实现延时任务的10种方法等等。但是这些文章基本上都是将方法大概的列举一下,给出部分示例代码,对于有经验的老程序员可能一看就知道该怎么去把它实现完整,但是对于初学者来说不够友好。所以,我打算写一个系列的文章,详细的给出每种延时任务的实现方法、完整实现代码,以及工作原理,欢迎并期待大家关注我

小概念:什么是延时任务?举个例子:你买了一张火车票,必须在30分钟之内付款,否则该订单被自动取消。订单30分钟不付款自动取消,这个任务就是一个延时任务。

一、DelayQueue的应用原理

DelayQueue是一个无界的BlockingQueue的实现类,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。

  • BlockingQueue即阻塞队列,java提供的面向多线程安全的队列数据结构,当队列内元素数量为0的时候,试图从队列内获取元素的线程将被阻塞或者抛出异常。
  • 这里的“无界”队列,是指队列的元素数量不存在上限,队列的容量会随着元素数量的增加而扩容。



DelayQueue实现了BlockingQueue接口,所以具有无界、阻塞的特点,除此之外它自己的核心特点就是:

  • 放入该队列的延时任务对象,只要到达延时时间之后才能被取到
  • DelayQueue 不接收null元素
  • DelayQueue 只接受那些实现了java.util.concurrent.Delayed接口的对象

二、订单延时任务的实现

了解了DelayQueue的特点之后,我们就可以利用它来实现延时任务了,实现java.util.concurrent.Delayed接口。

import org.jetbrains.annotations.NotNull;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit; /**
* 延时订单任务
*/
public class OrderDelayObject implements Delayed {
private String name;
private long delayTime; //延时时间
//实际业务中这里传订单信息对象,我这里只做demo,所以使用字符串了
private String order; public OrderDelayObject(String name, long delayTime, String order) {
this.name = name;
//延时时间加上当前时间
this.delayTime = System.currentTimeMillis() + delayTime;
this.order = order;
} //获取延时任务的倒计时时间
@Override
public long getDelay(TimeUnit unit) {
long diff = delayTime - System.currentTimeMillis();
return unit.convert(diff, TimeUnit.MILLISECONDS);
} //延时任务队列,按照延时时间元素排序,实现Comparable接口
@Override
public int compareTo(@NotNull Delayed obj) {
return Long.compare(this.delayTime, ((OrderDelayObject) obj).delayTime);
} @Override
public String toString() {
Date date = new Date(delayTime);
SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return "\nOrderDelayObject:{"
+ "name=" + name
+ ", time=" + sd.format(date)
+ ", order=" + order
+ "}";
}
}
  • 上文类中的order为订单信息对象,在实际的业务开发过程中应该是传递订单信息,用于取消订单业务的实现(订单30分钟不付款自动取消)。
  • Delayed接口继承自 Comparable接口,所以需要实现compareTo方法,用于延时任务在队列中按照“延时时间”进行排序。
  • getDelay方法是Delayed接口方法,实现该方法提供获取延时任务的倒计时时间

三、订单处理

首先我们需要一个容器,永久保存延时任务队列,如果是Spring开发环境我们可以这样做。

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

当用户下单的时候,将订单下单任务放入延时队列

@Resource
private DelayQueue<OrderDelayObject> orderDelayQueue; //发起订单下单的时候将订单演示对象放入orderDelayQueue
orderDelayQueue.add(
new OrderDelayObject(
"订单延时取消任务",
30 * 60 * 1000, //延时30分钟
"延时任务订单对象信息"
)
);

系统内开启一个线程,不断的从队列中获取消息,获取到之后对延时消息进行处理。DelayQueue的take方法从队列中获取延时任务对象,如果队列元素数量为0,或者没有到达“延时时间的任务”,该线程会被阻塞。

@Component
public class DelayObjectConsumer implements InitializingBean { @Resource
private DelayQueue<OrderDelayObject> orderDelayQueue; @Override
public void afterPropertiesSet() throws Exception {
while (true) {
OrderDelayObject task = orderDelayQueue.take();
System.out.println(task.toString());
System.out.println(task.getOrder());
//根据order订单信息,去查询该订单的支付信息
//如果用户没有进行支付,将订单从数据库中关闭
//如果订单并发量比较大,这里可以考虑异步或线程池的方式进行处理
}
}
}

需要说明的是,这里的while-true循环的延时任务处理时顺序执行的,在订单并发量比较大的时候,需要考虑异步处理的方式完成订单的关闭操作。我之前写作一个SpringBoot的可观测、易配置的线程池开源项目,可能会对你有帮助,源代码地址:https://gitee.com/hanxt/zimug-monitor-threadpool

经过我的测试,放入orderDelayQueue的延时任务,在半小时之后得到正确的执行处理。说明我们的实现是正确的。

四、优缺点

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

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

欢迎关注我的公告号:字母哥杂谈,回复003赠送作者专栏《docker修炼之道》的PDF版本,30余篇精品docker文章。字母哥博客:zimug.com

完整实现-通过DelayQueue实现延时任务的更多相关文章

  1. 延时任务-基于redis zset的完整实现

    所谓的延时任务给大家举个例子:你买了一张火车票,必须在30分钟之内付款,否则该订单被自动取消.订单30分钟不付款自动取消,这个任务就是一个延时任务. 我之前已经写过2篇关于延时任务的文章: <完 ...

  2. springboot执行延时任务-DelayQueue的使用

    DelayQueue简介 在很多场景我们需要用到延时任务,比如给客户异步转账操作超时后发通知告知用户,还有客户下单后多长时间内没支付则取消订单等等,这些都可以使用延时任务来实现. jdk中DelayQ ...

  3. 阿里面试官让我实现一个线程安全并且可以设置过期时间的LRU缓存,我蒙了!

    目录 1. LRU 缓存介绍 2. ConcurrentLinkedQueue简单介绍 3. ReadWriteLock简单介绍 4.ScheduledExecutorService 简单介绍 5. ...

  4. Java并发编程原理与实战三十六:阻塞队列&消息队列

    一.阻塞队列 1.阻塞队列BlockingQueue ---->可以理解成生产者消费者的模式---->消费者要等待到生产者生产出来产品.---->而非阻塞队列ConcurrentLi ...

  5. 细说并发5:Java 阻塞队列源码分析(下)

    上一篇 细说并发4:Java 阻塞队列源码分析(上) 我们了解了 ArrayBlockingQueue, LinkedBlockingQueue 和 PriorityBlockingQueue,这篇文 ...

  6. Java进阶知识点:不要只会写synchronized - JDK十大并发编程组件总结

    一.背景 提到Java中的并发编程,首先想到的便是使用synchronized代码块,保证代码块在并发环境下有序执行,从而避免冲突.如果涉及多线程间通信,可以再在synchronized代码块中使用w ...

  7. Java进阶知识点7:不要只会写synchronized - JDK十大并发编程组件总结

    一.背景 提到Java中的并发编程,首先想到的便是使用synchronized代码块,保证代码块在并发环境下有序执行,从而避免冲突.如果涉及多线程间通信,可以再在synchronized代码块中使用w ...

  8. Java基础教程(24)--集合

    一.Java集合框架   集合,有时也称为容器,是一个用来存储和管理多个元素的对象.Java中的集合框架定义了一套规范,用来表示和操作集合,使具体操作与实现细节解耦.集合框架都包含下列内容: 接口:这 ...

  9. java里面的队列

    非阻塞无界队列 ConcurrentLinkedQueue   public static void main(String[] args) throws InterruptedException { ...

随机推荐

  1. Ubuntu远程桌面助手(URDC)

    目前自动驾驶域控制器项目中使用了英伟达的Orin芯片+Ubuntu20.04系统.域控属于典型的Headless设备,开发调试时需要连接显示器(HDMI/DP).鼠标和键盘,或者使用NoMachine ...

  2. 快速全面了解QT软件界面开发技术

    快速全面了解QT软件界面开发技术     目录 前言 一. 学习QT可能的目的是什么? 只想体验一下QT? 当前的项目选择了用QT. 为将来做QT技术储备. 二. QT的核心技术优势是什么? QT在软 ...

  3. 学了WEB缓冲投毒-挖SRC的时候咋利用

    学了WEB缓冲投毒-挖SRC的时候咋利用 昨天发了哥WEB缓存投毒的学习文章,但是除了理论和训练营并无实践,正巧翻到了一篇文章,感觉还有点关系,转的一个国外的老哥的文章. 微信公众号:小惜渗透,欢迎大 ...

  4. python基础知识-day8(模块与包、random、os)

    1.模块与包 package:相同的模块代码存储在一个目录下(即包里边会包含多个模块).   包不能存储在文件夹的目录下,模块名称不能使用关键字.(不包含工程文件夹) 2.模块与包的实例 1)在工程文 ...

  5. Electron学习(三)之简单交互操作

    写在前面 最近一直在做批量测试工具的开发,打包的exe,执行也是一个黑乎乎的dos窗口,真的丑死了,总感觉没个界面,体验不好,所以就想尝试写桌面应用程序. 在技术选型时,Java窗体实现使用JavaF ...

  6. Linux操作系统(2):组管理和权限管理

    组管理和权限管理 Outline 1.查看文件所有者:ls -ahl 2.更改文件或目录权限命令:chmod 3.更改文件或目录所有者命令:chown 4.更改文件或目录所属组命令:chgrp 1)组 ...

  7. SpringMVC-02

    一.SSM整合[重点] 1 SSM整合配置 问题导入 请描述"SSM整合流程"中各个配置类的作用? 1.1 SSM整合流程 创建工程 SSM整合 Spring SpringConf ...

  8. 新版recon-ng安装模块

    表现:进入后报[*] No modules enabled/installed. 1.直接在recon-ng控制台输入marketplace install all,用来下载所有插件,如果不可以,往下 ...

  9. IDEA快捷键之晨讲篇

    IDEA之html快捷键 快捷键 释义 ! 生成HTML的初始格式 ---- ---- 标签名*n 生成n个相同的标签 ---- ---- 标签>标签 生成父子级标签(包含) ---- ---- ...

  10. day06 Java_数组_方法_参数

    精华笔记: 数组: 复制: System.arraycopy(a,1,b,0,4); int[ ] b = Arrays.copyOf(a,6); 排序: Arrays.sort(arr); //升序 ...