使用基于 Spring 注解的定时任务调度
工作中难免会有一些定时调度需求,比如定时统计数据,定时清理垃圾等等。你可能用过 Quartz 框架,但是现在基本上已经被淘汰了,因为其使用起来还是有些复杂。目前单机定时任务基本上都使用基于 Spring 注解的定时调度,分布式定时任务基本上都使用 xxl job 定时调度,原因就是使用起来很非常简单。
本篇博客主要介绍基于 Spring 注解的定时任务调度,特点就是使用简单,只需要掌握几个注解的使用就可以了,基本上已经可以满足绝大多数场景的需求。当然为了保证高可用,还是尽量使用 xxl job 分布式定时任务调度,这个后面再介绍。
在本篇博客的最后会提供源代码下载,话不多说,直接上代码介绍。
一、搭建工程
搭建一个 SpringBoot 工程,结构如下:
SingleThreadTask 类主要演示单线程定时调度,同一时刻只能执行一个任务。
MutiThreadTask 类主要演示多现场定时调度,各个任务互不影响,即使是同一时刻,也可以有多个任务执行。
工程的 pom 文件如下,除了引入 SpringBoot 的最小起步依赖之外,就没再引入别的 jar 包依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jobs</groupId> <artifactId>springboot_schedule</artifactId> <version>1.0</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.9.RELEASE</version> </parent> <dependencies> <!--引入最基本的 springboot 依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> </dependencies> </project>
项目工程的 application.yml 配置文件内容如下:(内容是自己随便定义的配置,配置 cron 表达式)
# 自己随便定义的 cron 表达式 myschedule: # 每隔 2 秒执行一次 cron1: 0/2 * * * * ? # 每隔 3 秒执行一次 cron2: 0/3 * * * * ?
在 SpringBoot 的启动类上需要添加注解,具体代码细节如下:
- 为了能够使定时任务生效,需要添加 @EnableScheduling 注解
- 为了能够使异步执行生效,需要添加 @EnableAsync 注解(异步执行是为了在相同时刻,同时执行多个任务)
package com.jobs; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; //此注解启用异步线程调用 @EnableAsync //此注解启用定时任务 @EnableScheduling @SpringBootApplication public class ScheduleApp { public static void main(String[] args) { SpringApplication.run(ScheduleApp.class, args); } }
二、单线程定时任务
代码都在 SingleThreadTask 类中,具体内容如下:
package com.jobs.schedule; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.time.LocalDateTime; @Component public class SingleThreadTask { /** * 特点:单线程调度,前面的任务没有执行完,即使到了下一次调度时间,也不会执行新任务 * 不管是在同一个类,还是在不同的类之间,同时配置了多个单线程调度任务, * 同一时间无法同时执行所配置的多个任务。 * 对于同一个任务来说,上次调度没执行完,下次调度时刻即使到了,也被直接忽略。 */ //每隔 2 秒调用一次,在代码中可以写死 //@Scheduled(cron = "0/2 * * * * ?") //从配置文件中,读取所配置的 cron 表达式 @Scheduled(cron = "${myschedule.cron1}") public void firstTask() { //休眠 5 秒 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("第 1 个SingleThreadTask任务调度时间:" + LocalDateTime.now()); } /* //每隔 2 秒调用一次,在代码中可以写死 //@Scheduled(cron = "0/2 * * * * ?") //从配置文件中,读取所配置的 cron 表达式 @Scheduled(cron = "${myschedule.cron1}") public void secondTask() { //休眠 5 秒 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("第 2 个SingleThreadTask任务调度时间:" + LocalDateTime.now()); } */ }
所谓单线程调度,特点就是:无论你配置多少个任务(在单个类中配置任务或者在多个类中配置任务),同一时刻只能运行一个任务。另外对于同一任务来说:前一次调度没执行完,下一次调度即使到了要执行的时间点,也不会执行,并且被直接忽略。
比如上面的代码,你把下面的代码注释取消,这样就可以执行两个任务了,由于我们配置的 cron 表达式相同,从配置文件中读取 myschedule.cron1 的配置内容,也就是每隔 2 秒执行一次任务,最终的结果是这两个任务交替执行,而无法同时执行,如下图:
每隔 2 秒执行一次任务,由于代码中明确要休眠 5 秒,所以 2 秒内方法中的代码肯定是执行不完的,因此方法需要 5 秒才能执行完。从上图中可以发现,两个任务执行完的时间间隔也是 5 秒。由于是单线程调度 2 个任务,因此他们交替执行,而不是并行执行。
三、多线程定时任务
代码都在 MutiThreadTask 类中,具体内容如下:
只需要在方法中增加一个注解 @Async 就可以了,该注解表示方法异步执行,也就是新创建一个新线程中执行
package com.jobs.schedule; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.time.LocalDateTime; @Component public class MutiThreadTask { /** * 特点:在方法上添加 @Async 注解,表示该方法新开线程执行,也就是异步执行 * 当然如果想让 @Async 注解生效,必须在程序入口的启动类上添加 @EnableAsync 注解 * 这样无论在相同类中,还是在不同的类之间,配置了多个定时任务,到了时间点就会自动执行 */ //该注解表示该方法异步执行 @Async //每隔 3 秒调用一次 //@Scheduled(cron = "0/3 * * * * ?") @Scheduled(cron = "${myschedule.cron2}") public void firstTask() { //休眠 5 秒 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("第 1 个MutiThreadTask任务调度时间:" + LocalDateTime.now()); } /* //该注解表示该方法异步执行 @Async //每隔 3 秒调用一次 //@Scheduled(cron = "0/3 * * * * ?") @Scheduled(cron = "${myschedule.cron2}") public void secondTask() { //休眠 5 秒 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("第 2 个MutiThreadTask任务调度时间:" + LocalDateTime.now()); } */ }
由于在方法上新增了 @Async 注解,因此每次调度时,方法都会新创建一个线程去执行,因此即使配置了多个定时任务,互相不会受到任何影响,也就是说在同一时刻可以执行多个定时调度任务。可以把上面代码中的注释取消掉,这样就配置了 2 个定时任务,执行效果如下:
从配置文件中读取的 cron 表达式是每隔 3 秒执行一次,由于代码中明确要休眠 5 秒,所以 2 秒内方法中的代码肯定是执行不完的,因此方法需要 5 秒才能执行完。但是由于线程都是异步执行的,所以多个定时调度任务之间互不影响,同时执行。
对于同一个任务来说,即使本次调度还没执行完,下次调度时间到了,就会立刻再创建一个新线程执行,因此从上图中可以发现,任务相隔时间就是 3 秒,跟配置的 cron 表达式的间隔时间相同。
OK,以上就是全部内容,基于 Spring 注解的定时任务调度已经介绍完毕。
在实际测试过程中,可以通过注释类上的 @Component 注解进行测试,比如想要测试单线程的运行效果,可以把 MutiThreadTask 类上的 @Component 注解注释掉,启动 SingleThreadTask 类上的 @Component 注解。
本篇博客的源代码地址为:https://files.cnblogs.com/files/blogs/699532/springboot_schedule.zip
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南