【小家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之后就被直接移除了,因此本处不再讲述

相关文章
|
28天前
|
存储 安全 Java
|
1月前
|
自然语言处理 JavaScript Java
Spring 实现 3 种异步流式接口,干掉接口超时烦恼
本文介绍了处理耗时接口的几种异步流式技术,包括 `ResponseBodyEmitter`、`SseEmitter` 和 `StreamingResponseBody`。这些工具可在执行耗时操作时不断向客户端响应处理结果,提升用户体验和系统性能。`ResponseBodyEmitter` 适用于动态生成内容场景,如文件上传进度;`SseEmitter` 用于实时消息推送,如状态更新;`StreamingResponseBody` 则适合大数据量传输,避免内存溢出。文中提供了具体示例和 GitHub 地址,帮助读者更好地理解和应用这些技术。
215 0
|
2月前
|
存储 数据采集 Java
Spring Boot 3 实现GZIP压缩优化:显著减少接口流量消耗!
在Web开发过程中,随着应用规模的扩大和用户量的增长,接口流量的消耗成为了一个不容忽视的问题。为了提升应用的性能和用户体验,减少带宽占用,数据压缩成为了一个重要的优化手段。在Spring Boot 3中,通过集成GZIP压缩技术,我们可以显著减少接口流量的消耗,从而优化应用的性能。本文将详细介绍如何在Spring Boot 3中实现GZIP压缩优化。
326 6
|
1月前
|
存储 NoSQL Java
Spring Boot项目中使用Redis实现接口幂等性的方案
通过上述方法,可以有效地在Spring Boot项目中利用Redis实现接口幂等性,既保证了接口操作的安全性,又提高了系统的可靠性。
39 0
|
3月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
46 0
Spring高手之路22——AOP切面类的封装与解析
|
3月前
|
JSON 安全 Java
|
3月前
|
存储 SQL Java
|
3月前
|
JavaScript Java Spring
Spring Boot 接口返回文件流
Spring Boot 接口返回文件流
125 0
|
20天前
|
存储 安全 关系型数据库
2024 Mysql基础与进阶操作系列之MySQL触发器详解(21)作者——LJS[你个小黑子这都还学不会嘛?你是真爱粉嘛?真是的 ~;以后请别侮辱我家鸽鸽]
MySQL触发器的使用场景之数据完整性约束、如何具体创建person的日志表、触发器与存储过程的对比与选择、触发器的性能和注意事项等具体操作详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
3月前
|
存储 关系型数据库 MySQL
MySQL 中的触发器数量之谜
【8月更文挑战第31天】
39 0