在 Spring Boot 中,@Scheduled 注解是实现定时任务最常用且便捷的方式,它基于 Spring 的 Task Scheduling 框架,支持多种触发规则,如固定间隔、固定延迟和 Cron 表达式。
基本使用步骤
-
启用定时任务 :在 Spring Boot 的主启动类或任意
@Configuration配置类上添加@EnableScheduling注解,以开启对@Scheduled注解的支持。@SpringBootApplication @EnableScheduling public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } -
创建定时任务 :在任意 Spring 管理的 Bean(如
@Component、@Service等)中,将需要定时执行的方法上添加@Scheduled注解,并指定触发规则。
@Scheduled 的触发方式
@Scheduled 注解提供了多种方式来定义任务执行的时间规则,必须且只能选择其中一种。
-
**
cron表达式(最灵活)**:使用 Cron 表达式可以精确控制任务在特定的时间点执行,例如每天、每周的特定时刻。@Component public class CronTask { // 每天凌晨 2 点执行 @Scheduled(cron = "0 0 2 * * ?") public void dailyTask() { System.out.println("每日定时任务执行: " + new Date()); } // 每 5 秒执行一次 @Scheduled(cron = "0/5 * * * * ?") public void everyFiveSeconds() { System.out.println("每5秒执行: " + new Date()); } } -
**
fixedRate(固定速率)** :从上一次任务开始执行的时间点 算起,每隔指定的毫秒数执行一次。无论上一次任务是否执行完毕,到时间点就会触发下一次执行。@Component public class FixedRateTask { // 每 5000 毫秒(5秒)执行一次,从方法开始执行时计时 @Scheduled(fixedRate = 5000) public void fixedRateTask() { System.out.println("固定速率任务执行: " + new Date()); try { Thread.sleep(3000); // 模拟任务执行耗时3秒 } catch (InterruptedException e) { e.printStackTrace(); } } } -
**
fixedDelay(固定延迟)** :从上一次任务执行结束的时间点算起,等待指定的毫秒数后,再执行下一次任务。这种方式能确保任务不会重叠。@Component public class FixedDelayTask { // 上一次任务执行结束后,等待 5000 毫秒(5秒)再执行下一次 @Scheduled(fixedDelay = 5000) public void fixedDelayTask() { System.out.println("固定延迟任务执行: " + new Date()); try { Thread.sleep(3000); // 模拟任务执行耗时3秒 } catch (InterruptedException e) { e.printStackTrace(); } } } -
**
initialDelay(初始延迟)** :通常与fixedRate或fixedDelay配合使用,用于指定任务在应用启动后,首次执行前的延迟时间。@Component
public class InitialDelayTask {
// 应用启动后延迟 10 秒,之后每隔 5 秒执行一次
@Scheduled(initialDelay = 10000, fixedRate = 5000)
public void initialDelayTask() {
System.out.println("初始延迟任务执行: " + new Date());
}
}
Cron 表达式语法
Cron 表达式由 6 或 7 个字段组成(秒 分 时 日 月 周 [年]),字段间用空格分隔。常用符号如下:
| 字段 | 允许值 | 特殊符号 | 说明 |
|---|---|---|---|
| 秒 | 0-59 | * / , - |
* 表示每秒,/ 表示间隔(如 5/15 表示 5, 20, 35, 50 秒),, 表示列举(如 5,20) |
| 分 | 0-59 | * / , - |
|
| 时 | 0-23 | * / , - |
|
| 日 | 1-31 | * / , - ? L W |
? 表示不指定,L 表示最后一天,W 表示最近的工作日 |
| 月 | 1-12 或 JAN-DEC | * / , - |
|
| 周 | 1-7 或 SUN-SAT | * / , - ? L # |
? 表示不指定,L 表示最后一天,# 表示第几个(如 6#3 表示每月第三个星期五) |
| 年 | 1970-2099 | * / , - |
可选 |
常见示例:
0 0 12 * * ?:每天中午 12:00:00 执行。0 15 10 ? * *:每天上午 10:15:00 执行。0 0/5 * * * ?:每 5 分钟执行一次。0 0 2 ? * 6L:每月最后一个星期五的凌晨 2:00:00 执行。0 0 12 15 * ?:每月 15 日中午 12:00:00 执行。
多线程与并发执行
默认情况下,Spring Boot 的 @Scheduled 任务是单线程串行执行的。这意味着如果一个任务执行时间过长,会阻塞其他任务的执行。
要实现多任务并发执行 ,需要自定义一个 TaskScheduler Bean,并设置线程池大小:
@Configuration
public class SchedulerConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10); // 设置线程池大小为10
scheduler.setThreadNamePrefix("scheduled-task-");
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.initialize();
return scheduler;
}
}
此外,如果任务本身是耗时操作(如调用外部 API),可以结合 @Async 注解实现异步执行,但这需要额外配置 @EnableAsync。
注意事项
- 时区问题 :Cron 表达式默认使用服务器的本地时区。如需指定时区,可使用
zone属性,例如@Scheduled(cron = "0 0 2 * * ?", zone = "Asia/Shanghai")。 - 分布式环境:在多个服务实例部署时,每个实例都会执行定时任务,可能导致任务重复执行。此时应考虑使用分布式调度框架(如 XXL-JOB、Quartz)或结合 Redis 分布式锁来保证任务的唯一性。
- 任务持久化 :
@Scheduled任务是内存级的,应用重启后任务信息会丢失。如需持久化,需集成 Quartz 等框架。