在Spring Boot中实现定时任务主要依赖 @Scheduled
注解和 @EnableScheduling
注解。以下是完整使用指南和时间规则详解:
一、快速启用定时任务
1. 主启动类开启支持
@SpringBootApplication
@EnableScheduling // 核心注解:启用定时任务
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2. 创建定时任务类
@Component // 必须声明为Spring组件
public class ScheduledTasks {
// 每5秒执行一次
@Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
System.out.println("定时任务执行: " + new Date());
}
}
二、六种时间规则配置方式
1. Cron表达式(最灵活)
格式:秒 分 时 日 月 周 [年]
字段范围:
字段 | 允许值 | 特殊符号 |
---|---|---|
秒 | 0-59 | , - * / |
分 | 0-59 | , - * / |
时 | 0-23 | , - * / |
日 | 1-31 | , - * ? / L W C |
月 | 1-12 或 JAN-DEC | , - * / |
周 | 1-7 或 SUN-SAT | , - * ? / L C # |
年 | 可选 1970-2099 | , - * / |
常用示例:
@Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行
@Scheduled(cron = "0 0/30 9-17 * * MON-FRI") // 工作日9:00-17:30每半小时
@Scheduled(cron = "0 15 10 L * ?") // 每月最后一天10:15执行
@Scheduled(cron = "0 0 12 ? * WED") // 每周三中午12点
符号说明:
*
: 所有值?
: 不指定(日/周冲突时使用)-
: 范围(如9-17)/
: 间隔(0/15每15秒)L
: 最后(日:月末;周:周六)W
: 最近工作日(15W:距离15日最近的工作日)#
: 第几个周几(6#3:每月第3个周五)
2. 固定速率(fixedRate)
@Scheduled(fixedRate = 5000) // 每5秒执行一次(从上次开始时间算)
public void fixedRateTask() {
// 任务代码
}
特性:
- 始终按固定频率执行,不等待上次任务完成
- 适用于独立任务(无状态)
3. 固定延迟(fixedDelay)
@Scheduled(fixedDelay = 3000) // 上次执行结束后3秒执行
public void fixedDelayTask() {
// 模拟耗时任务
Thread.sleep(1000);
}
特性:
- 严格保证任务间隔(含执行时间)
- 适用于有先后依赖的任务
4. 初始延迟(initialDelay + fixed)
@Scheduled(
initialDelay = 10000, // 应用启动10秒后首次执行
fixedRate = 60000 // 之后每1分钟执行
)
public void delayedStartTask() {
// 初始化类任务
}
5. 时间单位配置
支持时间单位参数:
@Scheduled(fixedRateString = "5000", timeUnit = TimeUnit.MILLISECONDS)
@Scheduled(fixedDelay = 2, timeUnit = TimeUnit.HOURS) // 每2小时
6. 属性文件配置(动态修改)
# application.properties
task.interval=0 0/5 * * * ?
@Scheduled(cron = "${task.interval}")
public void dynamicScheduleTask() {
// 动态读取配置
}
三、高级配置技巧
1. 线程池调优(避免单线程阻塞)
@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(
Executors.newScheduledThreadPool(10) // 自定义10线程池
);
}
}
2. 异常处理
@Scheduled(fixedRate = 5000)
public void taskWithException() {
try {
// 业务代码
} catch (Exception e) {
logger.error("定时任务异常", e); // 必须捕获异常
}
}
3. 分布式锁(集群环境)
@Scheduled(cron = "0 */1 * * * ?")
public void distributedTask() {
if(redisLock.tryLock("taskLock", 30)) { // Redis锁
try {
// 执行任务
} finally {
redisLock.unlock("taskLock");
}
}
}
四、避坑指南
问题场景 | 解决方案 |
---|---|
任务阻塞 | 增加线程池大小;异步执行 |
集群重复执行 | 添加分布式锁机制 |
配置不生效 | 检查是否添加 @EnableScheduling |
Cron表达式错误 | 使用 Cron在线验证工具 |
容器关闭任务继续 | 实现 DisposableBean 销毁任务 |
毫秒级精度不足 | 使用 ScheduledExecutorService |
五、监控方案(Spring Boot Actuator)
# application.yml
management:
endpoint:
scheduledtasks:
enabled: true
endpoints:
web:
exposure:
include: scheduledtasks
访问端点查看任务列表:
http://localhost:8080/actuator/scheduledtasks
六、完整示例
@Component
public class AdvancedScheduler {
// 工作日9点到18点,每15分钟执行
@Scheduled(cron = "0 0/15 9-18 * * MON-FRI")
public void businessHoursTask() {
// 业务处理
}
// 带初始延迟的每日备份
@Scheduled(
initialDelay = 30000,
fixedDelay = 24 * 60 * 60 * 1000
)
public void dailyBackup() throws Exception {
// 数据备份操作
}
// 使用属性配置动态间隔
@Scheduled(cron = "${backup.cron}")
public void dynamicBackup() {
// 动态任务
}
}
通过合理组合这些方法,即可实现各类复杂定时任务需求。建议优先使用Cron表达式实现灵活性,同时注意线程管理和异常处理确保稳定性。