SpringScheduling定时任务
Spring 提供了内置的任务调度支持,通过 @Scheduled 注解可以轻松实现定时任务。
启用定时任务
@Configuration @EnableScheduling public class SchedulingConfig { }
|
@Scheduled 注解
固定频率(fixedRate)
@Component public class FixedRateTask { @Scheduled(fixedRate = 5000) public void run() { System.out.println("固定频率执行: " + LocalDateTime.now()); } }
|
固定延迟(fixedDelay)
@Component public class FixedDelayTask { @Scheduled(fixedDelay = 3000) public void run() throws InterruptedException { System.out.println("开始执行: " + LocalDateTime.now()); Thread.sleep(2000); System.out.println("执行完成: " + LocalDateTime.now()); } }
|
初始延迟(initialDelay)
@Component public class InitialDelayTask { @Scheduled(initialDelay = 10000, fixedRate = 5000) public void run() { System.out.println("执行: " + LocalDateTime.now()); } }
|
Cron 表达式
@Component public class CronTask { @Scheduled(cron = "* * * * * *") public void everySecond() { System.out.println("每秒执行"); } @Scheduled(cron = "0 * * * * *") public void everyMinute() { System.out.println("每分钟执行"); } @Scheduled(cron = "0 0 * * * *") public void everyHour() { System.out.println("每小时执行"); } @Scheduled(cron = "0 0 2 * * *") public void dailyAt2AM() { System.out.println("每天凌晨2点执行"); } @Scheduled(cron = "0 0 8 * * MON") public void weeklyMonday() { System.out.println("每周一早上8点执行"); } @Scheduled(cron = "0 0 1 1 * *") public void monthly() { System.out.println("每月1号执行"); } }
|
Cron 表达式详解
秒 分 时 日 月 星期 [年]
* * * * * * 每秒 0 * * * * * 每分钟的0秒 0 0 * * * * 每小时的0分0秒 0 0 12 * * * 每天12点 0 0 12 * * ? 每天12点(?表示不指定星期) 0 15 10 ? * * 每天10:15 0 15 10 * * ? 每天10:15 0 15 10 * * ? 2024 2024年每天10:15 0 * 14 * * ? 每天14点的每分钟 0 0/5 14 * * ? 每天14点每5分钟 0 0/5 14,18 * * ? 每天14点和18点每5分钟 0 0-5 14 * * ? 每天14点的0-5分钟 0 15 10 ? * MON-FRI 工作日10:15 0 15 10 15 * ? 每月15日10:15 0 15 10 L * ? 每月最后一日10:15 0 15 10 ? * 6L 每月最后一个星期五10:15 0 15 10 ? * 6#3 每月第3个星期五10:15
|
| 字段 |
允许值 |
特殊字符 |
| 秒 |
0-59 |
, - * / |
| 分 |
0-59 |
, - * / |
| 时 |
0-23 |
, - * / |
| 日 |
1-31 |
, - * ? / L W |
| 月 |
1-12 |
, - * / |
| 星期 |
1-7 / SUN-SAT |
, - * ? / L # |
异步定时任务
@Configuration @EnableScheduling @EnableAsync public class AsyncSchedulingConfig { @Bean("taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(100); executor.setThreadNamePrefix("scheduled-"); executor.initialize(); return executor; } }
@Component public class AsyncScheduledTask { @Scheduled(fixedRate = 5000) @Async("taskExecutor") public void asyncRun() { System.out.println("异步执行: " + Thread.currentThread().getName()); } }
|
动态配置 Cron
@Component public class DynamicCronTask implements SchedulingConfigurer { @Autowired private TaskConfigRepository configRepository; @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.addTriggerTask( () -> executeTask(), triggerContext -> { String cron = configRepository.findCronExpression("myTask"); return new CronTrigger(cron).nextExecutionTime(triggerContext); } ); } public void executeTask() { System.out.println("动态定时任务执行: " + LocalDateTime.now()); } }
|
任务线程池配置
@Configuration public class SchedulingConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(taskExecutor()); } @Bean(destroyMethod = "shutdown") public Executor taskExecutor() { return Executors.newScheduledThreadPool(10); } }
|
常见问题
1. 任务重叠
@Component public class OverlappingTask { @Scheduled(fixedRate = 1000) public void run() throws InterruptedException { Thread.sleep(3000); } }
|
解决:使用 fixedDelay 或配置线程池
2. 异常处理
@Component public class SafeTask { @Scheduled(fixedRate = 5000) public void run() { try { doWork(); } catch (Exception e) { log.error("定时任务异常", e); } } }
|
3. 分布式环境
Spring Task 不支持分布式锁,多实例会重复执行。
解决:使用 Quartz + JDBC JobStore 或 ShedLock
<dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-spring</artifactId> <version>4.x</version> </dependency>
|
@Component public class DistributedTask { @Scheduled(fixedRate = 5000) @SchedulerLock(name = "distributedTask", lockAtMostFor = "10m", lockAtLeastFor = "1m") public void run() { System.out.println("分布式定时任务执行"); } }
|
Spring Task vs Quartz
| 特性 |
Spring Task |
Quartz |
| 复杂度 |
简单 |
复杂 |
| 持久化 |
不支持 |
支持(JDBC/内存) |
| 集群 |
不支持 |
支持 |
| 错过策略 |
简单 |
丰富 |
| 管理界面 |
无 |
有 |
| 适用场景 |
简单单机定时任务 |
复杂分布式调度 |
总结
| 场景 |
方案 |
| 简单定时任务 |
@Scheduled |
| 动态 Cron |
SchedulingConfigurer + TriggerTask |
| 异步执行 |
@Async + @Scheduled |
| 分布式定时 |
Quartz / ShedLock |
| 多线程 |
自定义线程池 |
Spring Task 适合简单的单机定时任务场景,复杂调度建议使用 Quartz。