SpringScheduling定时任务

SpringScheduling定时任务

Spring 提供了内置的任务调度支持,通过 @Scheduled 注解可以轻松实现定时任务。

启用定时任务

@Configuration
@EnableScheduling
public class SchedulingConfig {
}

@Scheduled 注解

固定频率(fixedRate)

@Component
public class FixedRateTask {

// 每5秒执行一次,不管上次是否完成
@Scheduled(fixedRate = 5000)
public void run() {
System.out.println("固定频率执行: " + LocalDateTime.now());
}
}

固定延迟(fixedDelay)

@Component
public class FixedDelayTask {

// 上次执行完成后,延迟3秒再执行
@Scheduled(fixedDelay = 3000)
public void run() throws InterruptedException {
System.out.println("开始执行: " + LocalDateTime.now());
Thread.sleep(2000); // 模拟耗时2秒
System.out.println("执行完成: " + LocalDateTime.now());
}
}

初始延迟(initialDelay)

@Component
public class InitialDelayTask {

// 启动后延迟10秒开始,然后每5秒执行
@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("每小时执行");
}

// 每天凌晨2点
@Scheduled(cron = "0 0 2 * * *")
public void dailyAt2AM() {
System.out.println("每天凌晨2点执行");
}

// 每周一早上8点
@Scheduled(cron = "0 0 8 * * MON")
public void weeklyMonday() {
System.out.println("每周一早上8点执行");
}

// 每月1号凌晨1点
@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 {

// 默认单线程,fixedRate可能导致任务堆积
@Scheduled(fixedRate = 1000)
public void run() throws InterruptedException {
Thread.sleep(3000); // 耗时3秒
}
}

解决:使用 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。


   转载规则


《SpringScheduling定时任务》 小乐 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录