点击下方“JavaEdge”,选择“设为星标”
第一时间关注技术干货!
免责声明~ 任何文章不要过度深思!万事万物都经不起审视,因为世上没有同样的成长环境,也没有同样的认知水平,更「没有适用于所有人的解决方案」; 别急着评判文章列出的观点,只需代入其中,适度审视一番自己即可,能「跳脱出来从外人的角度看看现在的自己处在什么样的阶段」才不为俗人。 怎么想、怎么做,全在乎自己「不断实践中寻找适合自己的大道」

即ThreadPoolTaskExecutor,但SpringBoot不同版本有区别。
@ConditionalOnClass(ThreadPoolTaskExecutor.class)
@Configuration
// 确保前缀为 spring.task.execution 的属性配置项被加载到 bean TaskExecutionProperties 中
@EnableConfigurationProperties(TaskExecutionProperties.class)
public class TaskExecutionAutoConfiguration {
/**
* Bean name of the application TaskExecutor.
*/
public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";
private final TaskExecutionProperties properties;
private final ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers;
private final ObjectProvider<TaskDecorator> taskDecorator;
public TaskExecutionAutoConfiguration(TaskExecutionProperties properties,
ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers,
ObjectProvider<TaskDecorator> taskDecorator) {
this.properties = properties;
this.taskExecutorCustomizers = taskExecutorCustomizers;
this.taskDecorator = taskDecorator;
}
// 定义 bean TaskExecutorBuilder taskExecutorBuilder
// 这是一个 TaskExecutor 构建器
@Bean
// 仅在该 bean 尚未被定义时才定义
@ConditionalOnMissingBean
public TaskExecutorBuilder taskExecutorBuilder() {
TaskExecutionProperties.Pool pool = this.properties.getPool();
TaskExecutorBuilder builder = new TaskExecutorBuilder();
builder = builder.queueCapacity(pool.getQueueCapacity());
builder = builder.corePoolSize(pool.getCoreSize());
builder = builder.maxPoolSize(pool.getMaxSize());
builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
builder = builder.keepAlive(pool.getKeepAlive());
builder = builder.threadNamePrefix(this.properties.getThreadNamePrefix());
builder = builder.customizers(this.taskExecutorCustomizers);
builder = builder.taskDecorator(this.taskDecorator.getIfUnique());
return builder;
}
// 懒惰模式定义 bean ThreadPoolTaskExecutor applicationTaskExecutor,
// 基于容器中存在的 TaskExecutorBuilder
@Lazy
// 使用bean名称 : taskExecutor, applicationTaskExecutor
@Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
// 仅在容器中不存在类型为 Executor 的 bean 时才定义
@ConditionalOnMissingBean(Executor.class)
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
return builder.build();
}
}SpringBoot 提供的自动配置类,简化 Spring 应用中异步任务执行和任务调度的配置。
自动配置异步任务执行器(Executor): 为应用配置一个默认的 ThreadPoolTaskExecutor,这是一个基于线程池的 TaskExecutor 实现。它适用于应用中通过 @Async 标注的异步方法的执行。如你不提供自定义配置,Spring Boot 将会使用这个自动配置的执行器来执行异步任务。
自动配置任务调度器(Scheduler): 为应用配置一个默认的 ThreadPoolTaskScheduler,这是用于任务调度的组件,支持 @Scheduled 注解标注的方法。这个调度器允许你在应用中简便地安排定期执行的任务。
可通过应用配置文件来定制配置: TaskExecutionAutoConfiguration 支持通过 application.properties 或 application.yml 配置文件来自定义任务执行和调度的相关参数,如线程池大小、队列容量等。这些参数分别位于 spring.task.execution 和 spring.task.scheduling 命名空间下。
与 Spring 生态系统的集成:自动配置的执行器和调度器与 Spring 的 @Async 和 @Scheduled 注解无缝集成
当你的应用需要执行异步任务或者按计划执行某些作业时,你通常会需要配置一个任务执行器或者任务调度器。在 Spring Boot 应用中,TaskExecutionAutoConfiguration 让这一步骤变得极为简单和自动化。
没有 TaskExecutionAutoConfiguration,你要手动配置线程池、异步执行器(TaskExecutor)和任务调度器(TaskScheduler)。即,你需要在你的 Spring 配置中显式地定义相关的Bean,并可能还要为这些Bean提供自定义的配置。
TaskExecutionAutoConfiguration 通过自动配置和简化配置工作,大大提高了开发效率,允许开发者更加专注于业务逻辑编写,而无需担心底层线程池和执行器的配置。
有TaskExecutionAutoConfiguration自动配置类,帮我们自动配置默认线程池ThreadPoolTaskExecutor。
这些参数可通过在application.properties或application.yml文件中设置来进行自定义调整。
在 Spring Boot 中,默认线程池由 TaskExecutorBuilder 类负责创建,它通常使用 ThreadPoolTaskExecutor 来配置默认的线程池。虽然默认的线程池参数可以根据不同的 Spring Boot 版本或特定配置而有所不同,但通常情况下,Spring Boot 默认的线程池参数如下:
SpringBoot默认的任务执行自动配置类。从@EnableConfigurationProperties(TaskExecutionProperties.class)可知,开启了属性绑定到TaskExecutionProperties类。

打个断点可见默认参数:

进入TaskExecutionProperties,属性绑定以 spring.task.execution 为前缀。默认线程池coreSize=8,最大线程数maxSize = Integer.MAX_VALUE,以及任务等待队列queueCapacity = Integer.MAX_VALUE。因此,要对异步任务的执行线程池做一些基础配置,以防OOM。
@ConfigurationProperties("spring.task.execution")
public class TaskExecutionProperties {
private String threadNamePrefix = "task-";
}
public static class Pool {
private int queueCapacity = Integer.MAX_VALUE;
private int coreSize = 8;
private int maxSize = Integer.MAX_VALUE;@Async默认用啥线程池?org.springframework.aop.interceptor.AsyncExecutionAspectSupport#determineAsyncExecutor
核心作用:根据@Async和方法上是否有指定的限定符来决定一个方法应该使用哪个AsyncTaskExecutor来执行异步任务。
protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
// 尝试从缓存中获取已确定的执行器
AsyncTaskExecutor executor = this.executors.get(method);
if (executor == null) {
Executor targetExecutor; // 声明一个目标执行器
String qualifier = getExecutorQualifier(method); // 获取方法上的@Async注解中指定的执行器的限定名
// 如果注解中有限定名则尝试根据该名字从BeanFactory中查找对应的执行器
if (StringUtils.hasLength(qualifier)) {
targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
}
else {
// 如果未指定限定名,则使用默认的执行器
targetExecutor = this.defaultExecutor;
// 如果默认执行器为空,则尝试从BeanFactory中获取默认的执行器
if (targetExecutor == null) {
synchronized (this.executors) {
// 双重检查锁定,确保只有一个线程可以初始化defaultExecutor
if (this.defaultExecutor == null) {
this.defaultExecutor = getDefaultExecutor(this.beanFactory);
}
targetExecutor = this.defaultExecutor;
}
}
}
// 如果没有合适的目标执行器,返回null
if (targetExecutor == null) {
return null;
}
// 如果目标执行器是AsyncListenableTaskExecutor则直接使用,否则使用适配器封装以确保它实现了AsyncListenableTaskExecutor
//所以SpringBoot2.1之前最终会找到org.springframework.core.task.SimpleAsyncTaskExecutor
executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
// 将确定的执行器放入缓存,以便下次直接使用
this.executors.put(method, executor);
}
// 返回确定的AsyncTaskExecutor
return executor;
}这个方法首先查找类型为 TaskExecutor 的Bean,如果有多个此类型的Bean,然后尝试查找名称为 “taskExecutor” 的Bean。如果这两种方式都没找到,那么将返回 null,表示没有找到适当的Bean来执行异步任务。这种情况下,AsyncExecutionInterceptor 将不会有执行器来处理异步方法。
// 会去 Spring 的容器中找有没有 TaskExecutor 或名称为 'taskExecutor' 为 Executor 的
@Nullable
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
if (beanFactory != null) {
try {
return beanFactory.getBean(TaskExecutor.class);
} catch (NoUniqueBeanDefinitionException ex) {
return beanFactory.getBean("taskExecutor", Executor.class);
} catch (NoSuchBeanDefinitionException ex) {
return beanFactory.getBean("taskExecutor", Executor.class);
}
}
return null;
}@Service
public class SyncService {
@Async
public void testAsync1() {
System.out.println(Thread.currentThread().getName());
ThreadUtil.sleep(10, TimeUnit.DAYS);
}
@Async
public void testAsync2() {
System.out.println(Thread.currentThread().getName());
ThreadUtil.sleep(10, TimeUnit.DAYS);
}
}注意需要在启动类上需要添加@EnableAsync,否则不会生效。
@RestController
public class TestController {
@Autowired
SyncService syncService;
@SneakyThrows
@RequestMapping("/testSync")
public void testTomcatThreadPool() {
syncService.testAsync1();
syncService.testAsync2();
}
}请求后观察控制台输出结果,打印线程名如下,这里采用TaskExecutionAutoConfiguration帮我们配置的默认线程池ThreadPoolTaskExecutor,就是以task-开头的
task-2
task-1Spring 基于AOP 实现了异步,因此,需要获取到容器中的代理对象才能具有异步功能。假定原对象叫obj,容器中的代理对象叫做 objproxy,在执行 fun() 会同时执行aop带来的其他方法。但是如果在fun() 中调用了 fun2(),那 fun2() 是原始的方法,而不是经过aop代理后的方法,就不会具有异步的功能。
2.1版本以后我们有TaskExecutionAutoConfiguration自动配置类,它会去配置默认的线程池,此时注入的ThreadPoolTaskExecutor就是默认的线程池。
@RestController
public class TestController {
@Resource
ThreadPoolTaskExecutor taskExecutor; //SpringBoot2.1之前直接注入会报错
@SneakyThrows
@RequestMapping("/testSync")
public void testTomcatThreadPool() {
ThreadPoolExecutor threadPoolExecutor = taskExecutor.getThreadPoolExecutor();
}
}通过debug可以看到默认的线程池参数如下,就是我们上面介绍的

最主要的就是此时我们没有TaskExecutionAutoConfiguration自动配置类,也就不会去配置默认的线程池,根据上面分析的determineAsyncExecutor方法,此时@Async会去采用默认的SimpleAsyncTaskExecutor。
@Service
public class SyncService {
@Async
public void testAsync1() {
System.out.println(Thread.currentThread().getName());
ThreadUtil.sleep(10, TimeUnit.DAYS);
}
@Async
public void testAsync2() {
System.out.println(Thread.currentThread().getName());
ThreadUtil.sleep(10, TimeUnit.DAYS);
}
}注意需要再启动类上需要添加@EnableAsync注解,否则不会生效。
@RestController
public class TestController {
@Autowired
SyncService syncService;
@SneakyThrows
@RequestMapping("/testSync")
public void testTomcatThreadPool() {
syncService.testAsync1();
syncService.testAsync2();
}
}验证输出结果,确实是采用了SimpleAsyncTaskExecutor线程池:
SimpleAsyncTaskExecutor-1
SimpleAsyncTaskExecutor-2最主要的就是此时我们没有TaskExecutionAutoConfiguration自动配置类,也就不会去配置默认的线程池,此时直接注入就会报错,所以需要自己定义!
@RestController
public class TestController {
@Resource
ThreadPoolTaskExecutor taskExecutor; //SpringBoot2.1之前直接注入会报错
@SneakyThrows
@RequestMapping("/testSync")
public void testTomcatThreadPool() {
ThreadPoolExecutor threadPoolExecutor = taskExecutor.getThreadPoolExecutor();
}
}此时发现在工程启动的时候就报错!

@Configuration
public class ThreadPoolConfiguration {
@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//设置线程池参数信息
taskExecutor.setCorePoolSize(10);
taskExecutor.setMaxPoolSize(50);
taskExecutor.setQueueCapacity(200);
taskExecutor.setKeepAliveSeconds(60);
taskExecutor.setThreadNamePrefix("myExecutor--");
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.setAwaitTerminationSeconds(60);
//修改拒绝策略为使用当前线程执行
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//初始化线程池
taskExecutor.initialize();
return taskExecutor;
}
}此时运行debug,可以看到不会启动报错,对应的线程池参数也是我们自定义的了。
注意当我们上面自定义了线程池疑惑,@Sync对应的线程池也是我们自定义的这个,不会再采用默认的SimpleAsyncTaskExecutor。

TaskExecutionAutoConfiguration? 默认参数如下:
有啥问题?
SimpleAsyncTaskExecutor 是Spring框架提供的一个简单的异步任务执行器,它并不是一个真正的线程池实现。它的主要特点是每次调用 #execute(Runnable) 方法时都会创建一个新的线程来执行任务。这意味着它不会重用线程,而是一次性的。
线程资源浪费: 每次执行任务时都创建新线程,这会导致线程的生命周期短暂,频繁地创建和销毁线程会消耗系统资源,包括CPU时间和内存。 系统资源耗尽: 在高并发场景下,如果大量任务同时提交,SimpleAsyncTaskExecutor 可能会创建大量线程,这可能会迅速耗尽系统资源,如线程数限制和内存资源。 缺乏线程管理: 真正的线程池通常包含线程管理机制,如线程重用、线程池大小控制、任务队列管理等。SimpleAsyncTaskExecutor 缺乏这些特性,因此在面对大量并发任务时可能无法有效地管理资源。 没有任务队列: 由于 SimpleAsyncTaskExecutor 不使用任务队列,它无法有效地缓冲任务。当任务提交速度超过执行速度时,新提交的任务可能会因为没有可用的线程而立即失败。 不适合长期运行的服务: 对于需要长期运行的服务,使用 SimpleAsyncTaskExecutor 可能会导致系统不稳定,因为它没有为长期运行的任务提供稳定的线程资源。
总结: SimpleAsyncTaskExecutor可简单地用于异步任务执行,但由于设计限制,不推荐生产环境使用,特别在要求高并发或资源使用高效的场景。
关注我,紧跟本系列专栏文章,咱们下篇再续!
★ 作者简介:魔都架构师,多家大厂后端一线研发经验,在分布式系统设计、数据平台架构和AI应用开发等领域都有丰富实践经验。 各大技术社区头部专家博主。具有丰富的引领团队经验,深厚业务架构和解决方案的积累。 负责:
目前主攻市级软件项目设计、构建服务全社会的应用系统。 ”
参考:
编程严选网:
http://www.javaedge.cn/专注分享软件开发全生态相关技术文章、视频教程资源、热点资讯等,全站资源免费学习,快来看看吧~
