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

文摘   2024-11-11 08:02   新疆  

最新实战案例锦集:《Spring Boot3实战案例合集》持续更新,每天至少更新一篇文章,订阅后将赠送文章最后展示的所有MD文档(学习笔记)。

环境: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环境中,我们可以直接注入该接口,如下示例:

@Resourceprivate TaskScheduler taskScheduler ;
@PostConstructpublic 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,或者以编程方式注册计划任务。如下示例:

@Componentpublic 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为什么不支持static字段注入的原因【源码分析】

自己动手实现Agent统计API接口调用耗时

SpringBoot+Nginx+Lua接口性能提升N倍

必学!Spring Boot结合MDC全方位的日志跟踪(支持跨线程)

Spring Boot 3太强:全新Controller接口定义方式

SpringBoot通过编程式事务提高系统性能

强烈建议JDK升级到17,因为这些功能

SpringBoot3.3.0版本发布了!速看都更新了什么?

从SpringBoot2.7开始自动配置发生哪些变化?你都知道吗?

Spring一个强大便捷的代理工厂类,你用过吗?

基于注解的Controller接口这些高级功能你都知道吗?

面试官:说说@Configuration与@Component有什么区别?

SpringBoot整合RSocket实时数据通信

请一定牢记SpringBoot项目开发中的8个扩展接口

Spring6.2震撼来袭,多线程实例化Bean应用启动速度飙升!

Spring全家桶实战案例源码
spring, springboot, springcloud 案例开发详解
 最新文章