文章目录
- 前言
- 一、@Scheduled是什么?
-
- [1.1 定时任务](#1.1 定时任务)
- [1.2 属性](#1.2 属性)
- 二、@Scheduled使用步骤
-
- [2.1 启动类增加@EnableScheduling](#2.1 启动类增加@EnableScheduling)
- [2.2 任务执行线程池配置](#2.2 任务执行线程池配置)
- [2.3 任务执行](#2.3 任务执行)
-
- [2.3.1 cron 表达式(最常用,复杂时间规则)](#2.3.1 cron 表达式(最常用,复杂时间规则))
- [2.3.2 fixedRate(固定频率,无视任务执行耗时)](#2.3.2 fixedRate(固定频率,无视任务执行耗时))
- [2.3.3 fixedDelay(固定延迟,等待上一次完成)](#2.3.3 fixedDelay(固定延迟,等待上一次完成))
- 三、注意事项:
-
- [3.1 定时任务 方法要求](#3.1 定时任务 方法要求)
- [3.2 分布式环境:](#3.2 分布式环境:)
- 总结
前言
在项目开发中,我们通常需要使用定时任务进行在一定的时间内进行任务的触发。
一、@Scheduled是什么?
它是 Spring 框架提供的定时任务核心注解,用于标记方法作为「定时执行的任务」,让开发者无需手动编写定时器(如 Timer/ScheduledExecutorService),只需通过简单配置就能实现任务的定时 / 周期性执行。
1.1 定时任务
将一个普通的 Spring Bean 方法标记为「定时任务」,在 Spring 启动类 / 配置类上添加 @EnableScheduling 注解,开启定时任务功能,Spring 容器会根据注解中的配置,自动、周期性地调用该方法。
优势:
✅ 无需手动管理线程池(Spring 内置线程池 taskScheduler);
✅ 支持多种时间配置方式(cron 表达式、固定延迟、固定频率);
✅ 支持时区配置、任务启停控制,适配各种定时场景;
✅ 与 Spring 容器深度集成,可直接使用依赖注入(如 @Autowired)。
1.2 属性
| 属性名 | 类型 | 作用 | 示例 |
|---|---|---|---|
| cron | String | 最灵活:基于 cron 表达式配置执行时间(支持复杂规则,如「每天 6-23 点每 30 分钟」) | cron = "0 */30 6-23 * * ?" |
| fixedRate | long | 固定频率:以上一次任务开始执行的时间为基准,每隔指定毫秒执行 | fixedRate = 30000(30 秒) |
| fixedDelay | long | 固定延迟:以上一次任务执行完成的时间为基准,延迟指定毫秒执行 | fixedDelay = 30000(30 秒) |
| initialDelay | long | 初始延迟:项目启动后,延迟指定毫秒才执行第一次任务(配合 fixedRate/fixedDelay) | initialDelay = 5000(5 秒) |
| zone | String | 时区:指定 cron 表达式的解析时区(如北京时间 Asia/Shanghai) | zone = "Asia/Shanghai" |
| timeUnit | TimeUnit | 时间单位:指定 fixedRate/fixedDelay/initialDelay 的单位(默认毫秒) | timeUnit = TimeUnit.SECONDS |
二、@Scheduled使用步骤
2.1 启动类增加@EnableScheduling
代码如下(示例):
java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
// 核心:添加 @EnableScheduling 开启定时任务
@SpringBootApplication
@EnableScheduling
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
2.2 任务执行线程池配置
Spring 定时任务默认使用 单线程池,若一个任务执行耗时过长,会阻塞后续所有任务(后续任务执行没有可用的线程导致任务无法执行);
解决:自定义线程池,提高并发能力
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@Configuration
@EnableScheduling
public class SchedulerConfig {
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10); // 核心线程数10
scheduler.setThreadNamePrefix("scheduled-task-"); // 线程名前缀
scheduler.setAwaitTerminationSeconds(60); // 关闭时等待60秒
scheduler.setWaitForTasksToCompleteOnShutdown(true); // 等待任务完成后关闭
return scheduler;
}
}
2.3 任务执行
2.3.1 cron 表达式(最常用,复杂时间规则)
适用于「指定时段、指定频率」的场景(如每天 6-23 点每 30 分钟执行、每周一凌晨 2 点执行)
java
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
// 必须是 Spring 管理的 Bean(@Component/@Service 等)
@Component
public class CronTask {
// 北京时间 6-23 点,每30分钟执行一次(秒 分 时 日 月 周)
@Scheduled(cron = "0 */30 6-23 * * ?", zone = "Asia/Shanghai")
public void executeCronTask() {
System.out.println("cron 定时任务执行:" + System.currentTimeMillis());
}
// 每周一凌晨 2:00 执行(? 表示不指定星期/日,避免冲突)
@Scheduled(cron = "0 0 2 ? * MON", zone = "Asia/Shanghai")
public void weeklyTask() {
System.out.println("每周一凌晨2点执行:" + System.currentTimeMillis());
}
}
2.3.2 fixedRate(固定频率,无视任务执行耗时)
适用于「不管上一次任务是否完成,到时间就执行」的场景(如每隔 30 秒执行,即使上一次任务耗时 20 秒)
执行时间线:启动 5 秒 → 第一次执行(耗时 20 秒)→ 第一次开始后 30 秒(即启动 35 秒)→ 第二次执行(无论第一次是否完成)。
java
@Component
public class FixedRateTask {
// 每隔30秒执行一次(初始延迟5秒,项目启动后5秒才第一次执行)
@Scheduled(fixedRate = 30000, initialDelay = 5000)
public void executeFixedRateTask() {
try {
// 模拟任务耗时20秒
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("fixedRate 任务执行:" + System.currentTimeMillis());
}
}
2.3.3 fixedDelay(固定延迟,等待上一次完成)
适用于「必须等上一次任务完成后,再延迟指定时间执行」的场景(如任务耗时不确定,避免并发执行)。
执行时间线:启动 5 秒 → 第一次执行(耗时 20 秒,启动 25 秒完成)→ 延迟 30 秒(启动 55 秒)→ 第二次执行。
java
@Component
public class FixedDelayTask {
// 上一次任务完成后,延迟30秒执行(时间单位显式指定为秒,更易读)
@Scheduled(fixedDelay = 30, initialDelay = 5, timeUnit = TimeUnit.SECONDS)
public void executeFixedDelayTask() {
try {
// 模拟任务耗时20秒
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("fixedDelay 任务执行:" + System.currentTimeMillis());
}
}
三、注意事项:
3.1 定时任务 方法要求
必须是 无返回值(void) 方法(返回值会被忽略);
必须是 无参数 方法(若需参数,可通过依赖注入获取,而非方法参数);
必须是 Spring Bean(需加 @Component/@Service 等注解,否则 Spring 无法扫描到)
3.2 分布式环境:
单机 @Scheduled 不支持分布式(多实例部署时,多个实例会同时执行任务);
解决:使用分布式定时任务框架(如 XXL-Job、Quartz 分布式版、Sentinel 限流),或基于 Redis 分布式锁实现任
务互斥。
总结
@Scheduled 是 Spring 实现本地定时任务的核心注解,标记方法为定时任务,支持 cron、固定频率、固定延迟三种时间规则;启动类加 @EnableScheduling,方法需是无参、void、Spring Bean;cron 适合复杂时间规则,zone 解决时区问题,自定义线程池避免任务阻塞;默认单线程、不支持分布式,需针对性优化。