一、引言
在 Spring Boot 中,定时任务可以通过 @Scheduled
注解来实现。你可以使用这个注解在方法上定义定时任务的执行频率。此外,Spring Boot 还允许你通过 SchedulingConfigurer
接口配置线程池,以便更好地管理并发执行的定时任务。
二、@Scheduled注解
1. 添加依赖
确保在 pom.xml
中添加了 Spring Boot Starter 依赖(通常已经包含在 Spring Boot 项目中):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
2. 启用定时任务
在主应用类或配置类上添加 @EnableScheduling
注解,以启用 Spring 的定时任务支持:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
3. 定义定时任务
使用 @Scheduled
注解定义定时任务。可以通过 fixedRate
、fixedDelay
或 cron
表达式来设置任务的执行频率。
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class MyScheduledTask {
// 每隔 5 秒执行一次
@Scheduled(fixedRate = 5000)
public void task1() {
System.out.println("Task 1 executed at " + System.currentTimeMillis());
}
// 每隔 5 秒执行一次,延迟 2 秒
@Scheduled(fixedDelay = 5000, initialDelay = 2000)
public void task2() {
System.out.println("Task 2 executed at " + System.currentTimeMillis());
}
// 使用 cron 表达式,每分钟执行一次
@Scheduled(cron = "0 * * * * ?")
public void task3() {
System.out.println("Task 3 executed at " + System.currentTimeMillis());
}
}
4. 注解的参数设置
在 Spring Boot 中,@Scheduled
注解用于标记一个方法,使其成为一个定时任务。@Scheduled
注解支持多个参数,以指定任务的执行策略。以下是一些常用的参数设置:
4.1 cron表达式
cron 表达式的格式如下:
[秒] [分] [小时] [日] [月] [周] [年](可选)
@Scheduled(cron = "0 15 10 * * ?")
public void executeTask() {
// 每天上午10点15分执行
}
字段的具体含义:
- 秒:0 到 59 之间的任何整数。
- 分:0 到 59 之间的任何整数。
- 小时:0 到 23 之间的任何整数。
- 日:0 到 31 之间的任何整数,表示一个月中的某一天。
- 月:1 到 12 之间的任何整数或三个字母的月份缩写(例如,
Jan
、Feb
、Mar
等)。- 周:0 到 7 之间的任何整数,其中 0 和 7 都表示星期日,或者使用三个字母的星期缩写(例如,
Sun
、Mon
、Tue
等)。- 年(可选):任何四位数。
每个字段可以包含以下类型的特殊字符:
- 特定值:例如,
0
、1
、15
。- 范围:使用
-
表示一个值的范围,例如1-3
表示1
、2
、3
。- 列表:使用
,
分隔多个值,例如1,2,3
。- 步长:使用
/
表示一个开始值和一个步长,例如0/15
表示从0
开始,每15
的倍数。- 通配符:使用
*
表示任何可能的值,相当于每天、每小时、每分钟、每秒。
示例
0 0/30 * * * ?
:每半小时执行一次。0 15 10 ? * *
:每天上午 10:15 执行一次。0 0 23 ? * *
:每天深夜 23:00 执行一次。0 0 10,14,16 * * ?
:每天上午 10:00、下午 2:00 和下午 4:00 各执行一次。0 0/5 14 * * ?
:从下午 2:00 开始,每 5 分钟执行一次,直到下午 2:55。0 0-5 14 * * ?
:在下午 2:00 到 2:05 之间每分钟执行一次。0 0 9-17 * * ?
:从上午 9:00 到下午 5:00 每小时执行一次。0 0/30 9-17 * * ?
:从上午 9:00 到下午 5:00 每半小时执行一次。0 0 9 ? * 1-5
:在工作日(周一到周五)的上午 9:00 执行一次。
请注意,cron 表达式的具体格式可能会根据使用的调度器实现而有所不同。上述示例适用于大多数标准的 cron 调度器。
4.2 fixedRate
指定上次任务执行结束后,再次执行任务之间的固定间隔时间(单位为毫秒)。
@Scheduled(fixedRate = 5000)
public void fixedRateTask() {
// 每5秒执行一次
}
4.3 fixedDelay
指定任务执行完毕后,延迟固定时间(单位为毫秒)后再次执行。
@Scheduled(fixedDelay = 5000)
public void fixedDelayTask() {
// 任务执行完毕后,延迟5秒再执行
}
4.4 initialDelay
指定首次执行任务前的延迟时间(单位为毫秒)。这个参数可以与 fixedRate
或 fixedDelay
结合使用。
@Scheduled(initialDelay = 1000, fixedRate = 5000)
public void initialDelayTask() {
// 首次执行延迟1秒,之后每5秒执行一次
}
4.5 zone
指定时区。如果不设置,默认使用系统默认时区。
@Scheduled(cron = "0 0 * * * ?", zone = "GMT+8")
public void executeTaskWithTimeZone() {
// 在指定时区下执行任务
}
5. 注意事项
在使用 @Scheduled
注解时,需要注意以下几点:
- 如果方法有参数,那么这些参数将被忽略,因为 Spring 无法确定如何传递这些参数。
- 如果方法返回值,那么这个返回值将被忽略。
- 定时任务默认是在单线程环境中执行的,这意味着所有的定时任务将按顺序一个接一个地执行。如果需要并发执行,需要配置多个任务或使用
@Async
注解。 - 定时任务在默认情况下是同步执行的,如果任务执行时间较长,可能会影响其他任务的执行。可以使用
@Async
注解将任务异步执行,但这需要额外的配置。
在实际的项目开发中,根据任务的具体需求选择合适的执行策略和参数设置,以确保定时任务能够正确、高效地执行。
三、SchedulingConfigurer 配置线程池
默认情况下,通过@Scheduled
注解定义的定时任务是单线程执行的。这意味着所有使用 @Scheduled
注解的方法都会在一个单独的线程中顺序执行。如果一个任务正在执行,其他任务将等待当前任务完成后再开始执行。而在实际的项目开发中,单线程定时任务的执行方式显然会造成性能和资源利用问题。
为了解决上述问题,可以通过实现 SchedulingConfigurer
接口来配置线程池:
1. 创建配置类
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.concurrent.Executor;
@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(15); // 设置线程池大小
taskScheduler.setThreadNamePrefix("scheduled-task-");
taskScheduler.initialize();
taskRegistrar.setScheduler(taskScheduler);
}
}
2. 说明
setPoolSize(int poolSize)
:设置线程池的大小。setThreadNamePrefix(String threadNamePrefix)
:设置线程名称的前缀,便于调试和监控。
四、总结
通过 @Scheduled
注解和 SchedulingConfigurer
接口,Spring Boot 提供了强大的定时任务功能。你可以根据具体需求配置任务的执行频率和线程池,以实现高效的定时任务管理。