速看!Spring Boot任务调度你不知道的使用技巧

开发 前端
我们可以通过实现SchedulingConfigurer接口来自定义相关任务调度的设置,这通常用于设置在执行计划任务时使用的特定TaskScheduler bean,或者以编程方式注册计划任务。

环境:SpringBoot3.2.5

1. TaskScheduler接口

Spring 提供了一个TaskSchedulerSPI,其中包含各种用于调度任务的方法,以便在未来某个时间点运行。下面的列表显示了 TaskScheduler 接口定义:

public interface TaskScheduler {
  ScheduledFuture schedule(Runnable task, Trigger trigger);
  ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period);
  ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);
}

上面接口只列出了核心方法。

在SpringBoot环境中,我们可以直接注入该接口,如下示例:

@Resource
private TaskScheduler taskScheduler ;


@PostConstruct
public void initScheduler() {
  this.taskScheduler.schedule(() -> {
    System.out.println("执行任务") ;
  }, new CronTrigger("*/2 * * * * *")) ;
}

上面通过cron表达式控制任务执行周期。你也可以设置固定的执行速率。

this.taskScheduler.scheduleAtFixedRate(() -> {
  System.out.println("固定周期指定任务") ;
}, Duration.ofSeconds(2)) ; // 每2s执行

在默认情况下SpringBoot创建的是ThreadPoolTaskScheduler

坑点:默认情况创建的ThreadPoolTaskScheduler只有一个线程,如果你当前有多个定时任务,如果出现重合那么任务会排队执行。通过如下参数修改线程池大小

spring:
  task:
    scheduling:
      thread-name-prefix: pack-task
      pool:
        size: 2

这里的spring.task.scheduling.pool.size默认值为:1

2. 基于注解任务调用

基于注解方式实现任务的调用应该是我们工作中应用的主要方式,非常简单方便,如下示例:

// 以固定的时间间隔执行带有注释的方法,该时间间隔从上一次调用结束到下一次调用开始。
@Scheduled(fixedDelay = 2000)
public void fixedDelayTask() throws Exception {
  System.err.printf("Current Time: %s, Current Thread: %s%n", new SimpleDateFormat("HH:mm:ss").format(new Date()), Thread.currentThread().getName()) ;
  TimeUnit.SECONDS.sleep(1) ;
}

输出结果

图片

虽然间隔设置为2s,但是实际输出是3s。

使用corn表达式

@Scheduled(cron = "*/3 * * * * *")
public void task1() {
  // TODO
}

每隔3s执行

响应式支持

从Spring Framework 6.1开始,几种类型的反应式方法也支持@Scheduled方法:

@Scheduled(fixedRate = 2, timeUnit = TimeUnit.SECONDS)
public Flux<Integer> reactiveTask() {
  return Flux.just(1, 2, 3).doOnNext(System.err::println) ;
}

在上面的示例中,会每隔2s打印1,2,3。

3. Cron表达式

一个格式良好的 cron 表达式(如 * * * * * *)由六个空格分隔的时间和日期字段组成,每个字段都有自己的有效值范围:

图片

特别说明:

  • 在范围(或 *)后面加上 /,表示数字值在范围内的间隔
  • 用连字符 (-) 分隔的两个数字表示一个数字范围。指定的范围是包含在内的。如(10-15 * * * * *):每分钟内的10,11,12,13,14,15秒时都会运行
  • 逗号 (,) 用于分隔列表中的项目;如(0 0 6,19 * * *)每天上午 6:00 和下午 7:00

写cron表达式不太好理解。为了提高可读性,Spring 支持以下表示常用序列的宏。因此,可以使用这些宏来代替六位数值:@Scheduled(cron = "@hourly")。

描述

@yearly (or @annually)

每年一次 (0 0 0 1 1 *)

@monthly

每月一次 (0 0 0 1 * *)

@weekly

每周一次 (0 0 0 * * 0)

@daily (or @midnight)

每天一次(0 0 0 * * *)

@hourly

每小时一次 (0 0 * * *)

如下示例:

@Scheduled(cron = "@hourly")
public void task2() {
  System.out.println("宏指令执行任务") ;
}

这样写简单多了。

4. 虚拟线程支持

从Spring6.1开始,支持虚拟线程(JDK21)执行任务的调用。在SpringBoot环境下你需要开启功能:

spring:
  threads:
    virtual:
      enabled: true

如下示例:

@Scheduled(cron = "*/3 * * * * *")
public void scheduler1() throws Exception {
  System.err.printf("当前时间: %s, 当前线程: %s, 是否虚拟线程: %b%n", new SimpleDateFormat("HH:mm:ss").format(new Date()), Thread.currentThread().getName(), Thread.currentThread().isVirtual()) ;
}

输出结果

图片图片

如果使用了虚拟线程,那么下面的配置将没有任何的意义。

spring:
  task:
    scheduling:
      pool:
        size: 10 #无意义

使用虚拟线程后,任务调用将使用单个调度线程,但每次执行计划任务时都会启动一个新线程(虚拟线程)。

5. 自定义任务调度配置

我们可以通过实现SchedulingConfigurer接口来自定义相关任务调度的设置,这通常用于设置在执行计划任务时使用的特定TaskScheduler bean,或者以编程方式注册计划任务。如下示例:

@Component
public class PackSchedulingConfigurer implements SchedulingConfigurer {


  @Override
  public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler() ;
    taskScheduler.setThreadNamePrefix("my-task-") ;
    taskScheduler.afterPropertiesSet();
    taskRegistrar.setTaskScheduler(taskScheduler );
  }
}

上面的示例修改任务调度执行的线程池对象。

public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
  taskRegistrar.addCronTask(() -> {
    System.out.println("动态注册调度任务...") ;
  }, "*/2 * * * * *");
}

上面示例,通过编程的方式动态注册调度任务。

责任编辑:武晓燕 来源: Spring全家桶实战案例源码
相关推荐

2020-08-11 11:20:49

Linux命令使用技巧

2010-03-03 16:26:10

ubantu使用技巧

2019-07-23 15:56:56

Spring Boot部署servlet

2019-07-23 17:52:59

Spring BootJava开发

2021-01-05 11:22:58

Python字符串代码

2020-01-29 19:40:36

Python美好,一直在身边Line

2015-08-13 09:03:14

调试技巧

2023-12-21 14:40:09

Python编程语言

2017-03-02 14:05:42

AndroidAndroid Stu调试技巧

2022-12-07 08:16:50

Vue 3技巧数组

2009-04-14 21:38:05

LinuxUbuntu技巧

2020-02-21 14:55:02

Python代码字符串

2021-01-28 10:04:40

JavaScript开发技术

2023-07-07 14:47:46

JavaScript技巧

2017-07-21 09:48:45

SQL索引查询

2020-06-12 09:20:33

前端Blob字符串

2020-07-28 08:26:34

WebSocket浏览器

2022-07-17 06:53:24

微服务架构

2020-10-21 09:36:40

Vue项目技巧

2017-10-10 13:58:38

前端CSS滤镜技巧
点赞
收藏

51CTO技术栈公众号