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的使用详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

(0)

相关推荐

  • SpringBoot定时任务两种(Spring Schedule 与 Quartz 整合 )实现方法

    前言 最近在项目中使用到定时任务,之前一直都是使用Quartz 来实现,最近看Spring 基础发现其实Spring 提供 Spring Schedule 可以帮助我们实现简单的定时任务功能. 下面说一下两种方式在Spring Boot 项目中的使用. Spring Schedule 实现定时任务 Spring Schedule 实现定时任务有两种方式 1. 使用XML配置定时任务, 2. 使用 @Scheduled 注解. 因为是Spring Boot 项目 可能尽量避免使用XML配置的形式,

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

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

  • 详解SpringBoot 创建定时任务(配合数据库动态执行)

    序言:创建定时任务非常简单,主要有两种创建方式:一.基于注解(@Scheduled) 二.基于接口(SchedulingConfigurer). 前者相信大家都很熟悉,但是实际使用中我们往往想从数据库中读取指定时间来动态执行定时任务,这时候基于接口的定时任务就大派用场了. 一.静态定时任务(基于注解) 基于注解来创建定时任务非常简单,只需几行代码便可完成. @Scheduled 除了支持灵活的参数表达式cron之外,还支持简单的延时操作,例如 fixedDelay ,fixedRate 填写相应

  • 详解Spring-Boot中如何使用多线程处理任务

    看到这个标题,相信不少人会感到疑惑,回忆你们自己的场景会发现,在Spring的项目中很少有使用多线程处理任务的,没错,大多数时候我们都是使用Spring MVC开发的web项目,默认的Controller,Service,Dao组件的作用域都是单实例,无状态,然后被并发多线程调用,那么如果我想使用多线程处理任务,该如何做呢? 比如如下场景: 使用spring-boot开发一个监控的项目,每个被监控的业务(可能是一个数据库表或者是一个pid进程)都会单独运行在一个线程中,有自己配置的参数,总结起来

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

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

  • Spring Boot启动过程(五)之Springboot内嵌Tomcat对象的start教程详解

    标题和Spring Boot启动过程(四)之Spring Boot内嵌Tomcat启动很像,所以特别强调一下,这个是Tomcat对象的. 从TomcatEmbeddedServletContainer的this.tomcat.start()开始,主要是利用LifecycleBase对这一套容器(engine,host,context及wrapper)进行启动并发布诸如configure_start.before_init.after_start的lifecycleEvent事件给相应的监听器(如

  • SpringBoot实现短信验证码校验方法思路详解

    有关阿里云通信短信服务验证码的发送,请参考我的另一篇文章   Springboot实现阿里云通信短信服务有关短信验证码的发送功能 思路 用户输入手机号后,点击按钮获取验证码.并设置冷却时间,防止用户频繁点击. 后台生成验证码并发送到用户手机上,根据验证码.时间及一串自定义秘钥生成MD5值,并将时间也传回到前端. 用户输入验证码后,将验证码和时间传到后台.后台先用当前时间减去前台传过来的时间验证是否超时.如果没有超时,就用用户输入的验证码 + 时间 + 自定义秘钥生成MD5值与之前的MD5值比较,

  • java中DelayQueue实例用法详解

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

  • Springboot常用注解及配置文件加载顺序详解

    Springboot常用注解及底层实现 1.@SpringBootApplication:这个注解标识了一个SpringBoot工程,她实际上是另外三个注解的组合,分别是: @SpringBootConfiguration:源码可以看到,这个注解除了元注解外,实际就只有一个@Configuration,把该类变成一个配置类,表示启动类也是一个配置类: @EnableAutoConfiguration:是开启自动配置的功能,向Spring容器中导入了一个Selector,用来加载ClassPath

  • SpringBoot Redis用注释实现接口限流详解

    目录 1. 准备工作 2. 限流注解 3. 定制 RedisTemplate 4. 开发 Lua 脚本 5. 注解解析 6. 接口测试 7. 全局异常处理 1. 准备工作 首先我们创建一个 Spring Boot 工程,引入 Web 和 Redis 依赖,同时考虑到接口限流一般是通过注解来标记,而注解是通过 AOP 来解析的,所以我们还需要加上 AOP 的依赖,最终的依赖如下: <dependency> <groupId>org.springframework.boot</g

  • Springboot配置返回日期格式化五种方法详解

    目录 格式化全局时间字段 1.前端时间格式化(不做无情人) 2.SimpleDateFormat格式化(不推荐) 3.DateTimeFormatter格式化(不推荐) 4.全局时间格式化(推荐) 实现原理分析 5.部分时间格式化(推荐) 总结 应急就这样 格式化全局时间字段 在yml中添加如下配置: spring.jackson.date-format=yyyy-MM-dd HH:mm:ss 或者 spring: jackson: ## 格式为yyyy-MM-dd HH:mm:ss date-

  • SpringBoot集成本地缓存性能之王Caffeine示例详解

    目录 引言 Spring Cache 是什么 集成 Caffeine 核心原理 引言 使用缓存的目的就是提高性能,今天码哥带大家实践运用 spring-boot-starter-cache 抽象的缓存组件去集成本地缓存性能之王 Caffeine. 大家需要注意的是:in-memeory 缓存只适合在单体应用,不适合与分布式环境. 分布式环境的情况下需要将缓存修改同步到每个节点,需要一个同步机制保证每个节点缓存数据最终一致. Spring Cache 是什么 不使用 Spring Cache 抽象

  • SpringBoot使用Shiro实现动态加载权限详解流程

    目录 一.序章 二.SpringBoot集成Shiro 1.引入相关maven依赖 2.自定义Realm 3.Shiro配置类 三.shiro动态加载权限处理方法 四.shiro中自定义角色与权限过滤器 1.自定义uri权限过滤器 zqPerms 2.自定义角色权限过滤器 zqRoles 3.自定义token过滤器 五.项目中会用到的一些工具类常量等 1.Shiro工具类 2.Redis常量类 3.Spring上下文工具类 六.案例demo源码 一.序章 基本环境 spring-boot 2.1

  • SpringBoot项目jar和war打包部署方式详解

    目录 jar与war jar包部署运行 war包部署运行 jar与war Spring Boot项目开发完成后,需要以jar或war的方式将项目打包部署到测试开发环境. jar即Java Archive,是Java归档文件,该文件格式与平台无关,它允许将许多文件组合成一个压缩文件.Java程序都可以打成jar包,目前Docker广泛使用,Java项目都会打成可执行的jar包,最终构建为镜像文件来运行. jar文件格式基于流行的ZIP文件格式.与ZIP文件不同的是,jar文件不仅用于压缩和发布,而

随机推荐