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

 更新时间:2019年12月12日 12:44:49   作者:依天照海  
DelayQueue是一个无界阻塞队列,只有在延迟期满时,才能从中提取元素。这篇文章主要介绍了springboot执行延时任务-DelayQueue的使用,需要的朋友可以参考下

DelayQueue简介

DelayQueue是一个无界阻塞队列,只有在延迟期满时,才能从中提取元素。
队列的头部,是延迟期满后保存时间最长的delay元素。

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

jdk中DelayQueue可以实现上述需求,顾名思义DelayQueue就是延时队列。

DelayQueue提供了在指定时间才能获取队列元素的功能,队列头元素是最接近过期的元素。

没有过期元素的话,使用poll()方法会返回null值,超时判定是通过getDelay(TimeUnit.NANOSECONDS)方法的返回值小于等于0来判断。

延时队列不能存放空元素。

一般使用take()方法阻塞等待,有过期元素时继续。

队列元素说明

DelayQueue<E extends Delayed>的队列元素需要实现Delayed接口,该接口类定义如下:

public interface Delayed extends Comparable<Delayed> {

 /**
  * Returns the remaining delay associated with this object, in the
  * given time unit.
  *
  * @param unit the time unit
  * @return the remaining delay; zero or negative values indicate
  * that the delay has already elapsed
  */
 long getDelay(TimeUnit unit);
}

所以DelayQueue的元素需要实现getDelay方法和Comparable接口的compareTo方法,getDelay方法来判定元素是否过期,compareTo方法来确定先后顺序。

springboot中实例运用

DelayTask就是队列中的元素

import java.util.Date;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class DelayTask implements Delayed {
 final private TaskBase data;
 final private long expire;
 /**
  * 构造延时任务
  * @param data  业务数据
  * @param expire 任务延时时间(ms)
  */
 public DelayTask(TaskBase data, long expire) {
  super();
  this.data = data;
  this.expire = expire + System.currentTimeMillis();
 }
 public TaskBase getData() {
  return data;
 }
 public long getExpire() {
  return expire;
 }
 @Override
 public boolean equals(Object obj) {
  if (obj instanceof DelayTask) {
   return this.data.getIdentifier().equals(((DelayTask) obj).getData().getIdentifier());
  }
  return false;
 }
 @Override
 public String toString() {
  return "{" + "data:" + data.toString() + "," + "expire:" + new Date(expire) + "}";
 }
 @Override
 public long getDelay(TimeUnit unit) {
  return unit.convert(this.expire - System.currentTimeMillis(), unit);
 }
 @Override
 public int compareTo(Delayed o) {
  long delta = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
  return (int) delta;
 }
}

TaskBase类是用户自定义的业务数据基类,其中有一个identifier字段来标识任务的id,方便进行索引

import com.alibaba.fastjson.JSON;
public class TaskBase {
 private String identifier;
 public TaskBase(String identifier) {
  this.identifier = identifier;
 }
 public String getIdentifier() {
  return identifier;
 }
 public void setIdentifier(String identifier) {
  this.identifier = identifier;
 }
 @Override
 public String toString() {
  return JSON.toJSONString(this);
 }
}

定义一个延时任务管理类DelayQueueManager,通过@Component注解加入到spring中管理,在需要使用的地方通过@Autowire注入

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Executors;
@Component
public class DelayQueueManager implements CommandLineRunner {
 private final Logger logger = LoggerFactory.getLogger(DelayQueueManager.class);
 private DelayQueue<DelayTask> delayQueue = new DelayQueue<>();
 /**
  * 加入到延时队列中
  * @param task
  */
 public void put(DelayTask task) {
  logger.info("加入延时任务:{}", task);
  delayQueue.put(task);
 }
 /**
  * 取消延时任务
  * @param task
  * @return
  */
 public boolean remove(DelayTask task) {
  logger.info("取消延时任务:{}", task);
  return delayQueue.remove(task);
 }
 /**
  * 取消延时任务
  * @param taskid
  * @return
  */
 public boolean remove(String taskid) {
  return remove(new DelayTask(new TaskBase(taskid), 0));
 }
 @Override
 public void run(String... args) throws Exception {
  logger.info("初始化延时队列");
  Executors.newSingleThreadExecutor().execute(new Thread(this::excuteThread));
 }
 /**
  * 延时任务执行线程
  */
 private void excuteThread() {
  while (true) {
   try {
    DelayTask task = delayQueue.take();
    processTask(task);
   } catch (InterruptedException e) {
    break;
   }
  }
 }
 /**
  * 内部执行延时任务
  * @param task
  */
 private void processTask(DelayTask task) {
  logger.info("执行延时任务:{}", task);
  //根据task中的data自定义数据来处理相关逻辑,例 if (task.getData() instanceof XXX) {}
 }
}

DelayQueueManager实现了CommandLineRunner接口,在springboot启动完成后就会自动调用run方法。

总结

以上所述是小编给大家介绍的springboot执行延时任务DelayQueue的使用详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

相关文章

  • Java实现世界上最快的排序算法Timsort的示例代码

    Java实现世界上最快的排序算法Timsort的示例代码

    Timsort 是一个混合、稳定的排序算法,简单来说就是归并排序和二分插入排序算法的混合体,号称世界上最好的排序算法。本文将详解Timsort算法是定义与实现,需要的可以参考一下
    2022-07-07
  • 解决poi导出excel无法打开文件的问题

    解决poi导出excel无法打开文件的问题

    这篇文章主要介绍了解决poi导出excel无法打开文件的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • 使用MockMvc进行controller层单元测试 事务自动回滚的完整案例

    使用MockMvc进行controller层单元测试 事务自动回滚的完整案例

    这篇文章主要介绍了使用MockMvc进行controller层单元测试 事务自动回滚的完整案例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Java日期时间使用方法汇总

    Java日期时间使用方法汇总

    这篇文章主要针对Java日期时间使用方法进行汇总,感兴趣的朋友可以参考一下
    2016-03-03
  • 详解java CountDownLatch和CyclicBarrier在内部实现和场景上的区别

    详解java CountDownLatch和CyclicBarrier在内部实现和场景上的区别

    这篇文章主要介绍了详解java CountDownLatch和CyclicBarrier在内部实现和场景上的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • 细数java for循环中的那些坑

    细数java for循环中的那些坑

    这篇文章主要介绍了Java for循环方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07
  • Spring计时器stopwatch使用详解

    Spring计时器stopwatch使用详解

    这篇文章主要介绍了Spring计时器stopwatch使用详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • Java设计模式之抽象工厂模式实例详解

    Java设计模式之抽象工厂模式实例详解

    这篇文章主要介绍了Java设计模式之抽象工厂模式,结合实例形式分析了抽象工厂模式的概念、功能、定义与使用方法,需要的朋友可以参考下
    2017-09-09
  • java不同版本在多线程中使用随机数生成器的实现

    java不同版本在多线程中使用随机数生成器的实现

    本文主要介绍了java不同版本在多线程中使用随机数生成器的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • MyBatis XPathParser解析器使用范例详解

    MyBatis XPathParser解析器使用范例详解

    这篇文章主要介绍了关于MyBatis中解析器XPathParser的实际使用实践,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2022-07-07

最新评论