1. 概述
在本篇文章中,我们将详细介绍如何使用 Spring 的 @Scheduled
注解 来配置和调度定时任务。
使用 @Scheduled
注解时,需要遵循以下简单规则:
- 方法的返回值应为
void
(如果非void
,返回值会被忽略) - 方法不能有参数
2. 启用定时任务支持
要在 Spring 中启用定时任务支持以及 @Scheduled
注解,可以通过 Java 配置类使用如下注解:
@Configuration
@EnableScheduling
public class SpringConfig {
...
}
如果你更喜欢 XML 配置,也可以使用:
<task:annotation-driven>
3. 固定延迟执行任务
我们先来看一个在上一次任务执行完成之后,延迟固定时间再执行的例子:
@Scheduled(fixedDelay = 1000)
public void scheduleFixedDelayTask() {
System.out.println(
"Fixed delay task - " + System.currentTimeMillis() / 1000);
}
✅ 说明:这种模式下,每次任务都会等待上一次任务执行完毕后再开始计时,适用于必须确保任务串行执行的场景。
4. 固定频率执行任务
接下来,我们配置一个按固定频率执行的任务:
@Scheduled(fixedRate = 1000)
public void scheduleFixedRateTask() {
System.out.println(
"Fixed rate task - " + System.currentTimeMillis() / 1000);
}
⚠️ 注意:默认情况下,Spring 的定时任务是单线程执行的,即使使用了 fixedRate
,也必须等上一个任务执行完才会触发下一个。
如果希望任务并发执行,需要加上 @Async
注解:
@EnableAsync
public class ScheduledFixedRateExample {
@Async
@Scheduled(fixedRate = 1000)
public void scheduleFixedRateTaskAsync() throws InterruptedException {
System.out.println(
"Fixed rate task async - " + System.currentTimeMillis() / 1000);
Thread.sleep(2000);
}
}
✅ 说明:加上 @Async
后,任务会并发执行,即使前一个任务还没结束,也会按频率继续触发。
5. fixedRate 与 fixedDelay 的区别
虽然都可以通过 @Scheduled
来调度任务,但 fixedRate
和 fixedDelay
的执行逻辑完全不同:
- fixedDelay:保证上一次任务执行结束后,再延迟 n 毫秒执行下一次任务。适用于必须串行执行的任务。
- fixedRate:每 n 毫秒执行一次任务,不关心上一次是否执行完成。适用于任务之间互不依赖的场景。
⚠️ 踩坑提示:如果任务执行时间超过频率间隔,fixedRate
可能导致内存或线程池耗尽。
6. 初始延迟执行任务
有时候我们希望任务第一次执行前有一个延迟时间,可以使用 initialDelay
:
@Scheduled(fixedDelay = 1000, initialDelay = 1000)
public void scheduleFixedRateWithInitialDelayTask() {
long now = System.currentTimeMillis() / 1000;
System.out.println(
"Fixed rate task with one second initial delay - " + now);
}
✅ 说明:第一次任务会在 initialDelay
时间后执行,之后按 fixedDelay
的规则继续调度。
7. 使用 Cron 表达式调度任务
对于更复杂的调度需求,可以使用 Cron 表达式:
@Scheduled(cron = "0 15 10 15 * ?")
public void scheduleTaskUsingCronExpression() {
long now = System.currentTimeMillis() / 1000;
System.out.println(
"schedule tasks using cron jobs - " + now);
}
✅ 说明:上述例子表示每月 15 日上午 10:15 执行任务。
默认使用服务器本地时区,但可以通过 zone
属性指定其他时区:
@Scheduled(cron = "0 15 10 15 * ?", zone = "Europe/Paris")
8. 外部化调度参数
硬编码调度参数虽然简单,但不便于维护。我们可以使用 Spring 表达式将配置外置:
@Scheduled(fixedDelayString = "${fixedDelay.in.milliseconds}")
@Scheduled(fixedRateString = "${fixedRate.in.milliseconds}")
@Scheduled(cron = "${cron.expression}")
✅ 说明:通过 properties 文件配置参数,可以实现无需重新部署即可调整任务调度策略。
9. 使用 XML 配置定时任务
Spring 也支持使用 XML 配置定时任务:
<!-- 配置调度器 -->
<task:scheduler id="myScheduler" pool-size="10" />
<!-- 配置任务 -->
<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="beanA" method="methodA"
fixed-delay="5000" initial-delay="1000" />
<task:scheduled ref="beanB" method="methodB"
fixed-rate="5000" />
<task:scheduled ref="beanC" method="methodC"
cron="*/5 * * * * MON-FRI" />
</task:scheduled-tasks>
10. 动态设置延迟或频率
Spring 中 @Scheduled
注解的参数在启动时就已经确定,无法在运行时动态修改。
但可以通过实现 SchedulingConfigurer
接口来实现动态调度:
@Configuration
@EnableScheduling
public class DynamicSchedulingConfig implements SchedulingConfigurer {
@Autowired
private TickService tickService;
@Bean
public Executor taskExecutor() {
return Executors.newSingleThreadScheduledExecutor();
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
taskRegistrar.addTriggerTask(
new Runnable() {
@Override
public void run() {
tickService.tick();
}
},
new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext context) {
Optional<Date> lastCompletionTime =
Optional.ofNullable(context.lastCompletionTime());
Instant nextExecutionTime =
lastCompletionTime.orElseGet(Date::new).toInstant()
.plusMillis(tickService.getDelay());
return Date.from(nextExecutionTime);
}
}
);
}
}
✅ 说明:通过 Trigger
动态计算下次执行时间,可以实现运行时调整调度频率。
11. 并行执行任务
默认情况下,Spring 使用单线程执行所有定时任务。如果希望并行执行,可以自定义 TaskScheduler
:
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(5);
threadPoolTaskScheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
return threadPoolTaskScheduler;
}
11.1. Spring Boot 中的配置
如果使用 Spring Boot,可以直接通过配置文件设置线程池大小:
spring.task.scheduling.pool.size=5
12. 总结
本文全面介绍了如何在 Spring 中使用 @Scheduled
注解来配置定时任务,包括固定延迟、固定频率、Cron 表达式、动态调度以及并行执行等内容。
示例代码可参考:GitHub 项目地址