关于java:完整实现通过DelayQueue实现延时任务

1次阅读

共计 3244 个字符,预计需要花费 9 分钟才能阅读完成。

实现延时工作有很多的办法,网上对于延时工作的实现的文章曾经不少了。比方:实现延时工作的 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…
通过我的测试,放入 orderDelayQueue 的延时工作,在半小时之后失去正确的执行解决。阐明咱们的实现是正确的。

四、优缺点

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

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

正文完
 0