整数软件

整数软件

SpringBoot Scheduled 定时任务

2025-01-06

默认情况下, SpringBoot Scheduled 是单线程执行定时任务的. 也就意味着, 如果一个任务执行时间较长, 线程会被阻塞. 如果此时有其他并发任务, 该任务将会被加入队列中排队处理, 直至有空闲线程来处理任务. 如果解决方案就是, 让 SpringBoot Scheduled 可以多线程执行定时任务.

配置线程池

1. 配置文件

默认情况下, 线程池数量是 1, 可以使用配置文件修改默认的线程池数量.

注意事项:

  1. 如果在配置文件设置了虚拟线程, 该参数将失效. 相关信息可以查看 TaskSchedulingProperties​ ​中 Pool​ ​的逻辑.

  1. 因为每个任务都会占用一个线程池数量, 若任务执行时间较长, 线程池数量被耗尽, 同样会出现没有空闲线程来处理任务的情况. 可以通过 @Async​ 等异步任务执行实际任务逻辑.

​application.yml​ 示例:

spring:
    task:
        scheduling:
            pool:
                size: 8

2. 实现 SchedulingConfigurer​ ​接口

通过实现 SchedulingConfigurer​ ​接口来自定义线程池配置, 是否开启虚拟线程不会影响定时任务的配置.

注意事项:

  1. 因为每个任务都会占用一个线程池数量, 若任务执行时间较长, 线程池数量被耗尽, 同样会出现没有空闲线程来处理任务的情况. 可以通过 @Async 等异步任务执行实际任务逻辑.

/**
 * 定时任务配置
 *
 * @author Toint
 * @date 2024/11/9
 */
@EnableScheduling
@Configuration
@Slf4j
public class ScheduledConfig implements SchedulingConfigurer {
    /**
     * 设置任务线程池
     */
    @Override
    public void configureTasks(@NonNull ScheduledTaskRegistrar taskRegistrar) {
        Assert.notNull(taskRegistrar, "taskRegistrar must not be null");
        final ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setVirtualThreads(true); // 使用虚拟线程执行任务
        threadPoolTaskScheduler.setPoolSize(RuntimeUtil.getProcessorCount() * 2); // 线程池数量为当前硬件核心数量的2倍
        threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);  // 等待所有任务完成再关闭线程池
        threadPoolTaskScheduler.setAwaitTerminationSeconds(60);  // 等待线程池终止的最长时间(秒)
        threadPoolTaskScheduler.initialize(); // 初始化
    }
}