实现延时工作有很多的办法,网上对于延时工作的实现的文章曾经不少了。比方:实现延时工作的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>();}

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

@Resourceprivate DelayQueue<OrderDelayObject> orderDelayQueue;//发动订单下单的时候将订单演示对象放入orderDelayQueueorderDelayQueue.add(        new OrderDelayObject(                "订单延时勾销工作",                30 * 60 * 1000,    //延时30分钟                "延时工作订单对象信息"        ));

零碎内开启一个线程,一直的从队列中获取音讯,获取到之后对延时音讯进行解决。DelayQueue的take办法从队列中获取延时工作对象,如果队列元素数量为0,或者没有达到“延时工夫的工作”,该线程会被阻塞。

@Componentpublic 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...
通过我的测试,放入orderDelayQueue的延时工作,在半小时之后失去正确的执行解决。阐明咱们的实现是正确的。

四、优缺点

应用DelayQueue实现延时工作非常简单,而且简便,全部都是规范的JDK代码实现,不必引入第三方依赖(不依赖redis实现、音讯队列实现等),十分的轻量级。

它的毛病就是所有的操作都是基于利用内存的,一旦呈现利用单点故障,可能会造成延时工作数据的失落。如果订单并发量十分大,因为DelayQueue是无界的,订单量越大,队列内的对象就越多,可能造成OOM的危险。所以应用DelayQueue实现延时工作,只实用于任务量较小的状况。
欢送关注我的布告号:字母哥杂谈,回复003赠送作者专栏《docker修炼之道》的PDF版本,30余篇精品docker文章。字母哥博客:zimug.com