定时任务执行操作是在日常开发中的一个非常常见的需求,通过定时任务操作我们可以实现一些后台处理任务,例如常见的数据清理、文件备份、定时邮件发送任务等功能。而在Spring Boot中默认提供了多种定时任务的执行方式,下面我们就来详细介绍一下在Spring Boot中最为常用的定时任务实现方式。
准备工作
首先我们需要创建一个Spring Boot的项目,并且在POM文件中,引入了定时任务所需要的相关配置依赖。通常情况下默认的定时任务支持依赖是Spring Boot默认自带的,所以只需要添加spring-boot-starter,不需要额外的配置依赖。
启用定时任务
在Spring Boot中并不会默认启动定时任务,所以需要在配置类或者是在主类上添加@EnableScheduling注解来开启定时任务执行操作,如下所示。
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);
}
}
这样我们就可以启动Spring的任务调度器,然后我们就可以通过@Scheduled注解来标记需要定时执行的方法,来实现定时执行任务的操作。
使用@Scheduled注解
@Scheduled注解是Spring提供的一种实现定时任务操作的方式,通过这个注解我们可以指定时间或者是通过Cron表达是来指定定时任务执行的频率,下面我们就来详细介绍一下这个注解的一些配置。
固定间隔执行
通过fixedRate属性,我们可以指定定时任务的执行的时间间隔,以毫秒为单位来间隔执行定时任务。如下所示,该代码逻辑会每隔5秒执行一次任务。
@Component
public class MyScheduledTask {
// 每隔5秒执行一次任务
@Scheduled(fixedRate = 5000)
public void performTask() {
System.out.println("任务执行了,当前时间:" + System.currentTimeMillis());
}
}
fixedRate指定的是上次任务开始执行的时间和下一次任务开始执行的时间之间的间隔。如果在此过程中任务执行的时间超过了间隔时间,任务则会并发执行。
固定延迟执行
通过fixedDelay属性,我们可以指定上次任务执行结束到下次任务执行开始之间的延迟时间,其执行单位也是毫秒。如下所示,
@Scheduled(fixedDelay = 5000)
public void performDelayedTask() {
System.out.println("延迟执行的任务,当前时间:" + System.currentTimeMillis());
}
这属性与fixedRate属性不同,它定义的是两次任务执行完成之后的间隔时间,也就是说如果一个任务执行的时间太长,那么就会等待当前任务执行完成之后,再去执行延迟时间之后的逻辑。
延迟执行(第一次延迟)
通过initialDelay属性,我们可以指定第一次执行任务的延迟时间,其单位也是毫秒,如下所示。
@Scheduled(initialDelay = 10000, fixedRate = 5000)
public void performDelayedStartTask() {
System.out.println("任务开始执行,当前时间:" + System.currentTimeMillis());
}
这任务指定了应用启动10秒之后开始执行定时任务,然后每隔五秒执行一次该任务。
使用Cron表达式执行
在@Scheduled注解中还支持了通过Cron表达式来定义任务执行的时间,这种方式要比上面的几种方式都灵活,这种方式适合于按照某种特定规律来执行任务的场景中,例如每天执行、每月执行、凌晨执行等等,如下所示。
@Scheduled(cron = "0 0 12 * * ?") // 每天中午12点执行
public void performCronTask() {
System.out.println("Cron表达式任务执行了,当前时间:" + System.currentTimeMillis());
}
Cron表达式格式如下
秒 (0-59)
分 (0-59)
小时 (0-23)
日 (1-31)
月 (1-12)
星期 (0-6,0代表星期天)
年份 (可选)
例如,"0 0 12 * * ?"表示每天中午12点执行,"0 15 10 ? * *"表示每天10:15执行。
定时任务的异常处理
在定时任务执行的过程中不可避免的可能会发生异常,而在Spring Boot中默认的日志记录并不会控制任务的停止执行,这个时候,就需要通过@Retry等配置机制来处理任务执行失败的操作。或者我们可以通过异常处理机制在执行方法中对异常处理进行捕获,如下所示。
@Scheduled(fixedRate = 5000)
public void performTaskWithErrorHandling() {
try {
// 模拟任务执行
System.out.println("任务开始执行...");
if (new Random().nextBoolean()) {
throw new RuntimeException("任务失败");
}
System.out.println("任务执行成功");
} catch (Exception e) {
System.err.println("任务执行失败,异常信息:" + e.getMessage());
}
}
定时任务的线程池配置
在Spring Boot中默认提供的定时任务是通过单线程执行的,也就是说如果出现多个任务并行执行就可能会造成任务执行的阻塞情况的发生,从而会影响到系统的性能,为了解决这种问题提高系统并发处理的能力,我们可以为定时任务配置线程池来提高定时任务的执行效率。
我们可以在application.properties或application.yml配置文件中对定时任务的线程池进行配置,如下所示。
spring.task.scheduling.pool.size=5 // 设置线程池大小
spring.task.scheduling.thread-name-prefix=scheduled-task- // 设置线程名前缀
或者我们可以通过自定义的线程池来试下精细化的定时任务管理。
@Configuration
public class TaskSchedulerConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5); // 设置线程池大小
scheduler.setThreadNamePrefix("scheduled-task-");
return scheduler;
}
}
总结
实现Spring Boot的定时任务操作是一件非常简单事情,只需要通过@Scheduled注解进行配置即可,其复杂度主要来自于线程池的配置以及线程的配置操作,为了避免阻塞任务,我们需要根据实际情况对定时任务的线程池进行配置调整,这样就可以实现高效的定时任务执行逻辑。