自定义 Spring 定时任务使用的线程池

1 背景

在 Spring 项目中,如果不手动配置定时任务线程池,那么所有的 @Scheduled 定时任务只能依靠同一个线程执行,因为默认的线程池的线程数为1。这意味着如果其中一个任务卡住了,其他所有定时任务都会被阻塞。为了提高并发能力,我们需要自定义Spring 定时任务使用的线程池。

2 方案

自定义 Spring 定时任务使用的线程池的方案案例如下所示:

java 复制代码
@Configuration
@EnableScheduling
public class SchedulingConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(Runtime.getRuntime().availableProcessors() * 2); // 设置最大并行任务数
        scheduler.setThreadNamePrefix("custom-scheduled-task-");
        scheduler.setWaitForTasksToCompleteOnShutdown(true); // 关闭 Spring 容器时等待任务完成
        scheduler.setAwaitTerminationSeconds(60); // 等待任务结束的最长时间
        scheduler.initialize(); // 必须初始化

        taskRegistrar.setTaskScheduler(scheduler);
    }
}

3 彩蛋

(1)Spring 定时任务使用 Spring 内置线程池的危害如下所示:

java 复制代码
1. 线程竞争和阻塞
串行执行风险:默认线程池线程数量有限,多个定时任务将排队执行
阻塞影响:耗时任务会阻塞其他任务的执行时机,影响整体调度准确性

2. 性能瓶颈
吞吐量限制:有限的线程数量无法充分利用系统资源
执行延迟:任务可能因线程不足而延迟执行,错过预期执行时间点

3. 资源争用
CPU密集型任务:多个CPU密集型任务在同一线程池中运行会导致资源争用
I/O密集型任务:阻塞性I/O操作会占用线程,影响其他任务执行

4. 异常传播
单点故障:一个任务的异常可能影响整个线程池的稳定性
任务丢失:严重异常可能导致后续任务无法正常执行

(2)Spring 定时任务依赖的线程池为 ThreadPoolTaskScheduler,其默认的线程数为1,源码如下所示。

定时任务配置入口(spring-boot-autoconfigure 包下)

java 复制代码
@ConditionalOnClass(ThreadPoolTaskScheduler.class)
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(TaskSchedulingProperties.class)
public class TaskSchedulingAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(SchedulingConfigurer.class)
    @ConditionalOnMissingBean(TaskScheduler.class)
    public ThreadPoolTaskScheduler taskScheduler(TaskSchedulerBuilder builder) {
        // 使用 builder 创建 ThreadPoolTaskScheduler
        return builder.build();
    }
}

定时任务类

java 复制代码
public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
		implements AsyncListenableTaskExecutor, SchedulingTaskExecutor, TaskScheduler {

	private volatile int poolSize = 1;

	private volatile boolean removeOnCancelPolicy = false;

	@Nullable
	private volatile ErrorHandler errorHandler;

	@Nullable
	private ScheduledExecutorService scheduledExecutor;

	......

	// 创建线程池
	@Override
	protected ExecutorService initializeExecutor(
			ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {

		this.scheduledExecutor = createExecutor(this.poolSize, threadFactory, rejectedExecutionHandler);

		if (this.removeOnCancelPolicy) {
			if (this.scheduledExecutor instanceof ScheduledThreadPoolExecutor) {
				((ScheduledThreadPoolExecutor) this.scheduledExecutor).setRemoveOnCancelPolicy(true);
			}
			else {
				logger.debug("Could not apply remove-on-cancel policy - not a ScheduledThreadPoolExecutor");
			}
		}

		return this.scheduledExecutor;
	}
	
	......
}
相关推荐
霸道流氓气质5 小时前
AI 流式对话与实时聊天平台:SSE 解码竞态、有状态会话隔离与背压取消的 Vue3 + Spring AI 生产级实战
java·人工智能·spring
frjc8 小时前
Spring AI 高级实战:ChatMemory、Tool Calling 与 MCP 架构解析
人工智能·spring·架构
T量子永生10 小时前
Unisoc(紫光展锐) Kernel 6.18 DDK 编译框架详解
linux·运维·spring
麻瓜code10 小时前
Spring AI 全模块 API 字典一览(1.1.0 版)
人工智能·windows·spring
养军博客11 小时前
Spring Security 4.x / 5.x / 6.x 配置全对比:前后端不分离与前后端分离
java·后端·spring·springsecurity
java_nnnn13 小时前
Spring IoC & DI 从入门到理解:控制反转、依赖注入与 Bean 管理详解
数据库·sql·spring
骑着蜗牛撵大象32713 小时前
Spring Boot 4.1 + Spring AI 2.0:RAG 供文、图谱供关系、MCP 供工具的 Java Agent 平台
java·spring boot·spring·微服务·知识图谱·rag·mcp
CoderYanger14 小时前
Java EE 进阶:6.SpringBoot 配置文件(第二弹)
java·spring boot·spring·面试·职场和发展·java-ee·程序员创富
m0_6399069414 小时前
SpringBoot 实现大模型统一调度网关|对接 OpenAI、Claude 协议差异与适配方案
java·spring boot·spring