Java DelayQueue实现延时任务的示例详解

目录
  • 一、DelayQueue的应用原理
  • 二、订单延时任务的实现
  • 三、订单处理
  • 四、优缺点

一、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的可观测、易配置的线程池开源项目,可能会对你有帮助,源代码地址

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

四、优缺点

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

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

到此这篇关于Java DelayQueue实现延时任务的示例详解的文章就介绍到这了,更多相关Java DelayQueue延时任务内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java并发中DelayQueue延迟队列原理剖析

    介绍 DelayQueue队列是一个延迟队列,DelayQueue中存放的元素必须实现Delayed接口的元素,实现接口后相当于是每个元素都有个过期时间,当队列进行take获取元素时,先要判断元素有没有过期,只有过期的元素才能出队操作,没有过期的队列需要等待剩余过期时间才能进行出队操作. 源码分析 DelayQueue队列内部使用了PriorityQueue优先队列来进行存放数据,它采用的是二叉堆进行的优先队列,使用ReentrantLock锁来控制线程同步,由于内部元素是采用的Priority

  • 详解Java中的延时队列 DelayQueue

    当用户超时未支付时,给用户发提醒消息.另一种场景是,超时未付款,订单自动取消.通常,订单创建的时候可以向延迟队列种插入一条消息,到时间自动执行.其实,也可以用临时表,把这些未支付的订单放到一个临时表中,或者Redis,然后定时任务去扫描.这里我们用延时队列来做.RocketMQ有延时队列,RibbitMQ也可以实现,Java自带的也有延时队列,接下来就回顾一下各种队列. Queue 队列是一种集合.除了基本的集合操作以外,队列还提供了额外的插入.提取和检查操作.队列的每个方法都以两种形式存在:一

  • Java多线程并发开发之DelayQueue使用示例

    在学习Java 多线程并发开发过程中,了解到DelayQueue类的主要作用:是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走.这种队列是有序的,即队头对象的延迟到期时间最长.注意:不能将null元素放置到这种队列中. Delayed,一种混合风格的接口,用来标记那些应该在给定延迟时间之后执行的对象.此接口的实现必须定义一个 compareTo 方法,该方法提供与此接口的 getDelay 方法一致的排序. 在网上看到了一些

  • 详解java中DelayQueue的使用

    简介 今天给大家介绍一下DelayQueue,DelayQueue是BlockingQueue的一种,所以它是线程安全的,DelayQueue的特点就是插入Queue中的数据可以按照自定义的delay时间进行排序.只有delay时间小于0的元素才能够被取出. DelayQueue 先看一下DelayQueue的定义: public class DelayQueue<E extends Delayed> extends AbstractQueue<E> implements Bloc

  • java DelayQueue的原理浅析

    在对DelayQueue延迟功能的使用上,很多人不能后完全理解延迟的一些功能使用,这里我们深入来挖掘一下DelayQueue的原理. 下面将从构造方法.接口.继承体系三个方面进行分析,需要注意的是,相较于其它的阻塞队列,DelayQueue因为延迟的功能多了接口的使用,一起来看具体内容. 1.构造方法 public DelayQueue() {} public DelayQueue(Collection<? extends E> c) { this.addAll(c); } 构造方法比较简单,

  • java中DelayQueue实例用法详解

    在阻塞队里中,除了对元素进行增加和删除外,我们可以把元素的删除做一个延迟的处理,即使用DelayQueue的方法.这里的删除需要一定的时间才能生效,有点类似于过期处理的理念.下面我们就DelayQueue的概念.特点进行讲解,然后在代码示例中体会DelayQueue的使用. 1.概念 是一个带有延迟时间的无界阻塞队列.队列中的元素,只有等延时时间到了,才能取出来.此队列一般用于过期数据的删除,或任务调度.以下,模拟一下定长时间的数据删除. 2.特点 (1)无边界设计 (2)添加(put)不阻塞,

  • springboot执行延时任务之DelayQueue的使用详解

    DelayQueue简介 DelayQueue是一个无界阻塞队列,只有在延迟期满时,才能从中提取元素. 队列的头部,是延迟期满后保存时间最长的delay元素. 在很多场景我们需要用到延时任务,比如给客户异步转账操作超时后发通知告知用户,还有客户下单后多长时间内没支付则取消订单等等,这些都可以使用延时任务来实现. jdk中DelayQueue可以实现上述需求,顾名思义DelayQueue就是延时队列. DelayQueue提供了在指定时间才能获取队列元素的功能,队列头元素是最接近过期的元素. 没有

  • Java DelayQueue实现延时任务的示例详解

    目录 一.DelayQueue的应用原理 二.订单延时任务的实现 三.订单处理 四.优缺点 一.DelayQueue的应用原理 DelayQueue是一个无界的BlockingQueue的实现类,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走. BlockingQueue即阻塞队列,java提供的面向多线程安全的队列数据结构,当队列内元素数量为0的时候,试图从队列内获取元素的线程将被阻塞或者抛出异常. 这里的“无界”队列,是指队列的元素数量不存在上限,队列的容量

  • Java 两种延时thread和timer详解及实例代码

    Java 两种延时thread和timer详解及实例代码 在Java中有时候需要使程序暂停一点时间,称为延时.普通延时用Thread.sleep(int)方法,这很简单.它将当前线程挂起指定的毫秒数.如 try { Thread.currentThread().sleep(1000);//毫秒 } catch(Exception e){} 在这里需要解释一下线程沉睡的时间.sleep()方法并不能够让程序"严格"的沉睡指定的时间.例如当使用5000作为sleep()方法的参数时,线 程

  • Java使用Collections.sort()排序的示例详解

    Java中Collections.sort()排序详解,通过实例代码给大家讲解,具体代码如下所示: public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("beijing"); list.add("shanghai"); list.add("hangzhou"); Collections.

  • java时区转换的理解及示例详解

    一.时区的基本概念 GMT(Greenwich Mean Time),即格林威治标准时,是东西经零度的地方.人们将地球人为的分为24等份,每一等份为一个时区,每时区横跨经度15度,时间正好为1小时.往西一个时区,则减去一小时:往东一个时区,则加上一小时.中国在东经120度上,(东经120°-东经0°)所得度数再除以15,即得8. UTC(Coordinated Universal Time),即世界协调时间,是经过平均太阳时(以格林威治时间GMT为准).地轴运动修正后的新时标以及以「秒」为单位的

  • Java使用FileInputStream流读取文件示例详解

    一.File流概念 JAVA中针对文件的读写操作设置了一系列的流,其中主要有FileInputStream,FileOutputStream,FileReader,FileWriter四种最为常用的流 二.FileInputStream 1)FileInputStream概念  FileInputStream流被称为文件字节输入流,意思指对文件数据以字节的形式进行读取操作如读取图片视频等 2)构造方法 2.1)通过打开与File类对象代表的实际文件的链接来创建FileInputStream流对象

  • Java数组的声明与创建示例详解

    今天在刷Java题的时候,写惯了C++发现忘记了Java数组的操作,遂把以前写的文章发出来温习一下. 首先,数组有几种创建方式? Java程序中的数组必须先进行初始化才可以使用,所谓初始化,就是为数组对象的元素分配内存空间,并为每个数组元素指定初始值,而在Java中,数组是静态的,数组一旦初始化,长度便已经确定,不能再随意更改. 声明数组变量 首先必须声明数组变量,才能在程序中使用数组.下面是声明数组变量的语法: dataType[] arrayRefVar; // 首选的方法 或 dataTy

  • Java实现图片裁剪功能的示例详解

    目录 前言 Maven依赖 代码 验证一下 前言 本文提供将图片按照自定义尺寸进行裁剪的Java工具类,一如既往的实用主义. Maven依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1.1-jre</version> </dependency> <dependen

  • Java实现经典大富翁游戏的示例详解

    目录 前言 主要设计 功能截图 代码实现 总结 前言 大富翁,又名地产大亨.是一种多人策略图版游戏.参与者分得游戏金钱,凭运气(掷骰子)及交易策略,买地.建楼以赚取租金.英文原名monopoly意为“垄断”,因为最后只得一个胜利者,其余均破产收场. <大富翁>游戏是用java语言实现,采用了swing技术进行了界面化处理,设计思路用了面向对象思想. 主要需求 可多人参与的大富翁游戏,玩家有初始资金,通过掷骰子,玩家移动指定骰子点数步骤,根据对应格子上的交易策略,来决定是赚钱还是亏钱,其他玩家破

  • Java设计模式之建造者模式的示例详解

    目录 定义 案例 需求 方案一 方案二 对比分析 总结 建造者模式的优势: 注意点 定义 建造者模式(Builder Pattern),又叫生成器模式,是一种对象构建模式 它可以将复杂对象的建造过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同表现的对象.建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可 以构建它们,用户不需要知道内部的具体构建细节. 案例 需求 女生每天化妆,假如只需要做发型,香水,衣服,并要求按照发型——>香水——>衣服的顺序进行,

  • Java设计模式之原型模式的示例详解

    目录 定义 案例 需求 方案一 方案二 对比分析 总结 定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象 即实现了一个原型接口,该接口用于创建当前对象的克隆,当直接创建对象的代价比较大时,则采用这种模式 案例 需求 张三要打印100000份照片 方案一 定义照片类 /** * 照片类 * @author:liyajie * @createTime:2022/2/15 11:47 * @version:1.0 */ @Data @AllArgsConstructor publi

随机推荐