2
头图

There are many ways to implement delayed tasks, and there are many articles about the implementation of delayed tasks on the Internet. For example: 10 ways to implement delayed tasks and so on. But these articles basically outline the methods and give some sample codes. For experienced programmers, they may know how to implement it completely at a glance, but they are not friendly enough for beginners. Therefore, I plan to write a series of articles, giving the implementation method, complete implementation code, and working principle of each delayed task in detail. Welcome and look forward to your attention .

Small concept: what is a delayed task? For example: you bought a train ticket, you must pay within 30 minutes, otherwise the order will be automatically cancelled. If the order is not paid for within 30 minutes, it will be automatically cancelled. This task is a delayed task.

1. Application principle of DelayQueue

DelayQueue is an implementation class of an unbounded BlockingQueue that is used to place objects that implement the Delayed interface, in which objects can only be removed from the queue when they expire.

  • BlockingQueue is a blocking queue, a multi-thread-safe queue data structure provided by java. When the number of elements in the queue is 0, the thread trying to get elements from the queue will be blocked or an exception will be thrown.
  • The "unbounded" queue here means that there is no upper limit on the number of elements in the queue, and the capacity of the queue will expand as the number of elements increases.


DelayQueue implements the BlockingQueue interface, so it has the characteristics of unbounded and blocking. In addition, its own core features are:

  • The delayed task objects put into the queue can only be retrieved after the delay time is reached .
  • DelayQueue does not receive null elements
  • DelayQueue only accepts objects that implement the java.util.concurrent.Delayed interface

Second, the realization of the order delay task

After understanding the characteristics of DelayQueue, we can use it to implement delay tasks and implement the java.util.concurrent.Delayed interface.

 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
            + "}";
  }
}
  • The order in the above class is the order information object. In the actual business development process, the order information should be transmitted, which is used to cancel the realization of the order business (the order will be automatically canceled without payment within 30 minutes).
  • The Delayed interface inherits from the Comparable interface, so the compareTo method needs to be implemented to sort the delayed tasks according to the "delay time" in the queue.
  • The getDelay method is the Delayed interface method. Implementing this method provides the countdown time for obtaining the delayed task.

3. Order processing

First of all, we need a container to permanently store the delayed task queue. If it is a Spring development environment, we can do this.

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

When the user places an order, put the order placing task into the delay queue

 @Resource
private DelayQueue<OrderDelayObject> orderDelayQueue;

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

A thread is started in the system to continuously obtain messages from the queue, and then the delayed messages are processed after being obtained. The take method of DelayQueue obtains the delayed task object from the queue. If the number of queue elements is 0, or the "delayed task" has not been reached, the thread will be blocked.

 @Component
public class DelayObjectConsumer<OrderDelayObject> implements InitializingBean {

  @Resource
  private DelayQueue<OrderDelayObject> orderDelayQueue;

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

It should be noted that the delayed tasks of the while-true loop here are executed sequentially. When the order concurrency is relatively large, the asynchronous processing method needs to be considered to complete the order closing operation. I wrote an observable and easy-to-configure thread pool open source project for SpringBoot before, which may be helpful to you. Source code address: https://gitee.com/hanxt/zimug-monitor-threadpool
After my test, the delay task placed in the orderDelayQueue was correctly executed after half an hour. shows that our implementation is correct.

Fourth, the advantages and disadvantages

Using DelayQueue to implement delay tasks is very simple and convenient. All of them are implemented by standard JDK code, without the introduction of third-party dependencies (not dependent on redis implementation, message queue implementation, etc.), which is very lightweight.

Its disadvantage is that all operations are based on application memory. Once an application single point of failure occurs, it may cause the loss of delayed task data. If the order concurrency is very large, because the DelayQueue is unbounded, the larger the order volume, the more objects in the queue, which may cause the risk of OOM. Therefore, using DelayQueue to implement delayed tasks is only suitable for cases where the amount of tasks is small.
Welcome to pay attention to my announcement number: Antetokounmpo, reply 003 and present the PDF version of the author's column "The Way of Docker Cultivation", more than 30 high-quality docker articles. Antetokounmpo Blog: zimug.com


字母哥博客
933 声望1.5k 粉丝