【小家Spring】Spring任务调度核心接口(类)之---TaskScheduler(任务调度器)、Trigger(触发器)、ScheduledTask(调度任务)详解(上)

简介: 【小家Spring】Spring任务调度核心接口(类)之---TaskScheduler(任务调度器)、Trigger(触发器)、ScheduledTask(调度任务)详解(上)

前言


先推荐阅读此篇:

【小家java】Java定时任务ScheduledThreadPoolExecutor详解以及与Timer、TimerTask的区别(执行指定次数停止任务)


某些时候我们可能需要在某些固定的时间或者是间隔一定的时间连续执行一些任务,如每天凌晨自动跑一些批次/心跳检测等。Spring通过使用TaskScheduler来完成这些功能。


任务调度框架设计到几个核心的接口,下面做如下介绍。


任务调度和JDK的定时器、线程池有关,推荐先阅读上面的{相关阅读}


TriggerContext


该接口表示触发的上下文。它能够获取上次任务原本的计划时间/实际的执行时间以及实际的完成时间

//@since 3.0 我们发现每个方法都有可能返回null(比如首次执行)
public interface TriggerContext {
  // 上次预计的执行时间
  @Nullable
  Date lastScheduledExecutionTime();
  // 上次真正执行时间
  @Nullable
  Date lastActualExecutionTime();
  // 上次完成的时间
  @Nullable
  Date lastCompletionTime();
}


它的唯一实现类:SimpleTriggerContext,源码就不用看了,没有任何逻辑,只有一些赋值操作。


Trigger



TaskScheduler中将会使用到Trigger对象,所以先对它进行分析

Trigger接口用于计算任务的下次执行时间。它的接口定义如下:


public interface Trigger {
  //获取下次执行时间
  @Nullable
  Date nextExecutionTime(TriggerContext triggerContext);
}


image.png


它有如上的两个实现类。


CronTrigger


顾名思义,它通过Cron表达式来生成调度计划。比如:


scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));

以上表达式表示在工作日的9-17点之间,每隔15分钟执行一次;


Spring对cron表达式的支持,是由CronSequenceGenerator来实现的,不依赖于别的框架。下面给出一个Demo感受下:

    public static void main(String[] args) {
        CronSequenceGenerator generator = new CronSequenceGenerator("0 15 * * * MON-FRI");
        Date next = generator.next(new Date());
        System.out.println(next); //Mon Apr 22 17:15:00 CST 2019
        System.out.println(generator.next(next)); //Mon Apr 22 18:15:00 CST 2019
    }


这个类若我们自己需要解析Cron表达式,也是可以拿出来使用的。

CronTrigger的原理也比较简单,主要是实现了nextExecutionTime方法:


  @Override
  public Date nextExecutionTime(TriggerContext triggerContext) {
    Date date = triggerContext.lastCompletionTime();
    // 这里面有个处理:如果data为null,相当于任务已经完成了
    if (date != null) {
      // 拿到上一次预定执行的时间
      Date scheduled = triggerContext.lastScheduledExecutionTime();
      // 如果预定执行的时间为null(比如第一次)或者上一次还在data之后,那就取当前时间嘛
      if (scheduled != null && date.before(scheduled)) {
        // Previous task apparently executed too early...
        // Let's simply use the last calculated execution time then,
        // in order to prevent accidental re-fires in the same second.
        date = scheduled;
      }
    }
    // 如果任务还没有完成,那就以当前时间去计算下一个时间
    else {
      date = new Date();
    }
    return this.sequenceGenerator.next(date);
  }


PeriodicTrigger


用于定期执行的Trigger;它有两种模式:


  • fixedRate:两次任务开始时间之间间隔指定时长
  • fixedDelay: 上一次任务的结束时间与下一次任务开始时间``间隔指定时长


可见这两种情况的区别就在于,在决定下一次的执行计划时是否要考虑上次任务在什么时间执行完成。 默认情况下PeriodicTrigger使用了fixedDelay模式。


  • period: long类型,表示间隔时长,注意在fixedRate与fixedDelay两种模式下的不同含义
  • timeUnit: TimeUnit类型,表示间隔时长的单位,如毫秒等;默认是毫秒
  • initialDelay: long类型,表示启动任务后间隔多长时间开始执行第一次任务
  • fixedRate: boolean类型,表示是否是fixedRate,为True时是fixedRate,否则是fixedDelay,默认为False



TaskScheduler


Spring任务调度器的核心接口,定义了执行定时任务的主要方法,主要根据任务的不同触发方式调用不同的执行逻辑,其实现类都是对JDK原生的定时器或线程池组件进行包装,并扩展额外的功能。


TaskScheduler用于对Runnable的任务进行调度,它包含有多种触发规则。


public interface TaskScheduler {
  // 提交任务调度请求 
  // Runnable task:待执行得任务
  // Trigger trigger:使用Trigger指定任务调度规则
  @Nullable
  ScheduledFuture<?> schedule(Runnable task, Trigger trigger);
  // @since 5.0  这里使用的Instant 类,其实最终也是转换成了Date
  default ScheduledFuture<?> schedule(Runnable task, Instant startTime) {
    return schedule(task, Date.from(startTime));
  }
  //  提交任务调度请求   startTime表示它的执行时间
  //  注意任务只执行一次,使用startTime指定其启动时间  
  ScheduledFuture<?> schedule(Runnable task, Date startTime);
  // @since 5.0
  default ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) {
    return scheduleAtFixedRate(task, Date.from(startTime), period.toMillis());
  }
  // 使用fixedRate的方式提交任务调度请求    任务首次启动时间由传入参数指定 
  // task 待执行的任务  startTime 任务启动时间    period 两次任务启动时间之间的间隔时间,默认单位是毫秒
  ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period);
  // @since 5.0
  default ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period) {
    return scheduleAtFixedRate(task, period.toMillis());
  }
  // 使用fixedRate的方式提交任务调度请求 任务首次启动时间未设置,任务池将会尽可能早的启动任务
  // task 待执行任务 
  // period 两次任务启动时间之间的间隔时间,默认单位是毫秒
  ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period);
  // @since 5.0
  default ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) {
    return scheduleWithFixedDelay(task, Date.from(startTime), delay.toMillis());
  }
  //  使用fixedDelay的方式提交任务调度请求  任务首次启动时间由传入参数指定 
  // delay 上一次任务结束时间与下一次任务开始时间的间隔时间,单位默认是毫秒 
  ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
  // @since 5.0
  default ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay) {
    return scheduleWithFixedDelay(task, delay.toMillis());
  }
  // 使用fixedDelay的方式提交任务调度请求 任务首次启动时间未设置,任务池将会尽可能早的启动任务 
  // delay 上一次任务结束时间与下一次任务开始时间的间隔时间,单位默认是毫秒 
  ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay);
}


它有如下实现类:


image.png


备注:TaskScheduler的另一实现类TimerManagerTaskScheduler在Spring5.0之后就被直接移除了,因此本处不再讲述

相关文章
|
5月前
|
监控 Java BI
《深入理解Spring》定时任务——自动化调度的时间管理者
Spring定时任务通过@Scheduled注解和Cron表达式实现灵活调度,支持固定频率、延迟执行及动态配置,结合线程池与异常处理可提升可靠性,适用于报表生成、健康检查等场景,助力企业级应用自动化。
|
6月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
556 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
XML Java 数据格式
探索Spring之利剑:ApplicationContext接口
本文深入介绍了Spring框架中的核心接口ApplicationContext,解释了其作为应用容器的功能,包括事件发布、国际化支持等,并通过基于XML和注解的配置示例展示了如何使用ApplicationContext管理Bean实例。
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
645 73
|
自然语言处理 JavaScript Java
Spring 实现 3 种异步流式接口,干掉接口超时烦恼
本文介绍了处理耗时接口的几种异步流式技术,包括 `ResponseBodyEmitter`、`SseEmitter` 和 `StreamingResponseBody`。这些工具可在执行耗时操作时不断向客户端响应处理结果,提升用户体验和系统性能。`ResponseBodyEmitter` 适用于动态生成内容场景,如文件上传进度;`SseEmitter` 用于实时消息推送,如状态更新;`StreamingResponseBody` 则适合大数据量传输,避免内存溢出。文中提供了具体示例和 GitHub 地址,帮助读者更好地理解和应用这些技术。
2707 123
|
11月前
|
前端开发 IDE Java
Spring MVC 中因导入错误的 Model 类报错问题解析
在 Spring MVC 或 Spring Boot 开发中,若导入错误的 `Model` 类(如 `ch.qos.logback.core.model.Model`),会导致无法解析 `addAttribute` 方法的错误。正确类应为 `org.springframework.ui.Model`。此问题通常因 IDE 自动导入错误类引起。解决方法包括:删除错误导入、添加正确包路径、验证依赖及清理缓存。确保代码中正确使用 Spring 提供的 `Model` 接口以实现前后端数据传递。
407 0
|
Java Spring
【Spring配置相关】启动类为Current File,如何更改
问题场景:当我们切换类的界面的时候,重新启动的按钮是灰色的,不能使用,并且只有一个Current File 项目,下面介绍两种方法来解决这个问题。
308 10
|
存储 NoSQL Java
Spring Boot项目中使用Redis实现接口幂等性的方案
通过上述方法,可以有效地在Spring Boot项目中利用Redis实现接口幂等性,既保证了接口操作的安全性,又提高了系统的可靠性。
679 1
|
存储 数据采集 Java
Spring Boot 3 实现GZIP压缩优化:显著减少接口流量消耗!
在Web开发过程中,随着应用规模的扩大和用户量的增长,接口流量的消耗成为了一个不容忽视的问题。为了提升应用的性能和用户体验,减少带宽占用,数据压缩成为了一个重要的优化手段。在Spring Boot 3中,通过集成GZIP压缩技术,我们可以显著减少接口流量的消耗,从而优化应用的性能。本文将详细介绍如何在Spring Boot 3中实现GZIP压缩优化。
2056 6