在 Spring 中,使用 @Scheduled 注解来定义定时任务时,默认情况下定时任务是同步执行的,也就是说,如果同一时间有两个定时任务,Spring 是通过一个单线程来顺序执行这些任务的。即当一个定时任务开始执行时,其他任务需要等待前一个任务执行完成后再执行。
一、默认情况下的行为
默认情况下,Spring 中的 @Scheduled 任务是由单线程执行器管理的。这意味着即使多个定时任务在相同的时间触发,它们也会按照顺序一个接一个执行,而不是并发执行。
例子
@Component
public class MyScheduledTasks {
@Scheduled(fixedDelay = 5000)
public void task1() throws InterruptedException {
System.out.println("Task 1 started at: " + new Date());
Thread.sleep(7000); // 模拟任务执行时间较长
System.out.println("Task 1 finished at: " + new Date());
}
@Scheduled(fixedDelay = 2000)
public void task2() {
System.out.println("Task 2 started at: " + new Date());
}
}
在上面的代码中,task1 和 task2 是两个定时任务。假设 task1 需要 7 秒来执行,但它的定时周期为 5 秒;同时 task2 每 2 秒触发一次。
在默认的单线程环境中,task2 会在 task1 执行完成之后再执行。这种行为导致即使 task2 本应每 2 秒运行一次,但它也会被延迟,直到 task1 完成。
二、如何使定时任务异步执行?
如果希望多个定时任务能够并发执行,而不是顺序执行,可以通过配置多线程任务执行器来解决这个问题。具体方法是配置一个自定义的任务执行器,将 @Scheduled 的任务调度切换到多线程环境。
2.1 解决方案:配置线程池
配置一个 TaskScheduler:
你可以通过创建一个自定义的 TaskScheduler 来指定线程池的大小,从而使定时任务异步执行。
@Configuration
@EnableScheduling
public class SchedulingConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(10); // 设置线程池大小
taskScheduler.setThreadNamePrefix("ScheduledTask-");
taskScheduler.initialize();
taskRegistrar.setTaskScheduler(taskScheduler);
}
}
在上面的代码中,我们创建了一个 ThreadPoolTaskScheduler,并设置了线程池大小为 10。这意味着可以同时有 10 个定时任务并发执行。
2.2 @Async 注解
除了使用线程池,还可以通过 @Async 注解来使特定的定时任务异步执行。
@Component
public class MyScheduledTasks {
@Async
@Scheduled(fixedDelay = 5000)
public void task1() throws InterruptedException {
System.out.println("Task 1 started at: " + new Date());
Thread.sleep(7000); // 模拟任务执行时间较长
System.out.println("Task 1 finished at: " + new Date());
}
@Async
@Scheduled(fixedDelay = 2000)
public void task2() {
System.out.println("Task 2 started at: " + new Date());
}
}
要使 @Async 注解生效,需要在 Spring Boot 应用的主类或配置类上加上 @EnableAsync 注解:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
三、总结
● 默认情况下,Spring 的 @Scheduled 定时任务是由单线程调度器管理的,所以是同步执行的。如果多个任务在同一时间触发,它们会按顺序一个接一个地执行。
● 如果需要并发执行定时任务,可以配置多线程的任务执行器(如 ThreadPoolTaskScheduler),或者使用 @Async 注解结合线程池来实现异步执行。