springboot默认线程池配置
最近看到项目有配置如下,直接使用@Async会调用自己配置的线程池,重新翻一下代码并记录
配置位置
@Bean("taskExecutor")public Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 核心线程数 - 埋点业务相对较轻,设置较小的核心线程数executor.setCorePoolSize(3);// 最大线程数executor.setMaxPoolSize(8);// 队列容量 - 设置较大的队列,允许更多任务排队executor.setQueueCapacity(200);// 线程名前缀executor.setThreadNamePrefix("task-progress-");// 当线程池已满时,使用调用者所在的线程来执行任务,确保埋点不丢失executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 线程空闲时间executor.setKeepAliveSeconds(60);// 等待所有任务结束后再关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true);// 初始化executor.initialize();return executor;}
spring源码调用异步线程的位置代码
//org.springframework.aop.interceptor.AsyncExecutionAspectSupport#getDefaultExecutor@Nullableprotected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {if (beanFactory != null) {try {// Search for TaskExecutor bean... not plain Executor since that would// match with ScheduledExecutorService as well, which is unusable for// our purposes here. TaskExecutor is more clearly designed for it.return beanFactory.getBean(TaskExecutor.class);}catch (NoUniqueBeanDefinitionException ex) {logger.debug("Could not find unique TaskExecutor bean. " +"Continuing search for an Executor bean named 'taskExecutor'", ex);try {return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);}catch (NoSuchBeanDefinitionException ex2) {if (logger.isInfoEnabled()) {logger.info("More than one TaskExecutor bean found within the context, and none is named " +"'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly " +"as an alias) in order to use it for async processing: " + ex.getBeanNamesFound());}}}catch (NoSuchBeanDefinitionException ex) {logger.debug("Could not find default TaskExecutor bean. " +"Continuing search for an Executor bean named 'taskExecutor'", ex);try {return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);}catch (NoSuchBeanDefinitionException ex2) {logger.info("No task executor bean found for async processing: " +"no bean of type TaskExecutor and no bean named 'taskExecutor' either");}// Giving up -> either using local default executor or none at all...}}return null;}
spring创建异步线程池的代码位置
@ConditionalOnClass(ThreadPoolTaskExecutor.class)
@AutoConfiguration
@EnableConfigurationProperties(TaskExecutionProperties.class)
public class TaskExecutionAutoConfiguration {/*** Bean name of the application {@link TaskExecutor}.*/public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";@Bean@ConditionalOnMissingBeanpublic TaskExecutorBuilder taskExecutorBuilder(TaskExecutionProperties properties,ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers,ObjectProvider<TaskDecorator> taskDecorator) {TaskExecutionProperties.Pool pool = 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());Shutdown shutdown = properties.getShutdown();builder = builder.awaitTermination(shutdown.isAwaitTermination());builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());builder = builder.threadNamePrefix(properties.getThreadNamePrefix());builder = builder.customizers(taskExecutorCustomizers.orderedStream()::iterator);builder = builder.taskDecorator(taskDecorator.getIfUnique());return builder;}@Lazy@Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })@ConditionalOnMissingBean(Executor.class)//AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME 就是taskExecutorpublic ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {return builder.build();}}
很明显如果没有ThreadPoolTaskExecutor spring默认就会创建一个,并且配置了两个bean名称都是指向ThreadPoolTaskExecutor
现在重点看一下默认的配置
/** Copyright 2012-2019 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.boot.autoconfigure.task;import java.time.Duration;import org.springframework.boot.context.properties.ConfigurationProperties;/*** Configuration properties for task execution.** @author Stephane Nicoll* @author Filip Hrisafov* @since 2.1.0*/
@ConfigurationProperties("spring.task.execution")
public class TaskExecutionProperties {private final Pool pool = new Pool();private final Shutdown shutdown = new Shutdown();/*** Prefix to use for the names of newly created threads.*/private String threadNamePrefix = "task-";public Pool getPool() {return this.pool;}public Shutdown getShutdown() {return this.shutdown;}public String getThreadNamePrefix() {return this.threadNamePrefix;}public void setThreadNamePrefix(String threadNamePrefix) {this.threadNamePrefix = threadNamePrefix;}public static class Pool {/*** Queue capacity. An unbounded capacity does not increase the pool and therefore* ignores the "max-size" property.*/private int queueCapacity = Integer.MAX_VALUE;/*** Core number of threads.*/private int coreSize = 8;/*** Maximum allowed number of threads. If tasks are filling up the queue, the pool* can expand up to that size to accommodate the load. Ignored if the queue is* unbounded.*/private int maxSize = Integer.MAX_VALUE;/*** Whether core threads are allowed to time out. This enables dynamic growing and* shrinking of the pool.*/private boolean allowCoreThreadTimeout = true;/*** Time limit for which threads may remain idle before being terminated.*/private Duration keepAlive = Duration.ofSeconds(60);public int getQueueCapacity() {return this.queueCapacity;}public void setQueueCapacity(int queueCapacity) {this.queueCapacity = queueCapacity;}public int getCoreSize() {return this.coreSize;}public void setCoreSize(int coreSize) {this.coreSize = coreSize;}public int getMaxSize() {return this.maxSize;}public void setMaxSize(int maxSize) {this.maxSize = maxSize;}public boolean isAllowCoreThreadTimeout() {return this.allowCoreThreadTimeout;}public void setAllowCoreThreadTimeout(boolean allowCoreThreadTimeout) {this.allowCoreThreadTimeout = allowCoreThreadTimeout;}public Duration getKeepAlive() {return this.keepAlive;}public void setKeepAlive(Duration keepAlive) {this.keepAlive = keepAlive;}}public static class Shutdown {/*** Whether the executor should wait for scheduled tasks to complete on shutdown.*/private boolean awaitTermination;/*** Maximum time the executor should wait for remaining tasks to complete.*/private Duration awaitTerminationPeriod;public boolean isAwaitTermination() {return this.awaitTermination;}public void setAwaitTermination(boolean awaitTermination) {this.awaitTermination = awaitTermination;}public Duration getAwaitTerminationPeriod() {return this.awaitTerminationPeriod;}public void setAwaitTerminationPeriod(Duration awaitTerminationPeriod) {this.awaitTerminationPeriod = awaitTerminationPeriod;}}}
异步定时任务的线程池配置
/** Copyright 2012-2019 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.boot.autoconfigure.task;import java.time.Duration;import org.springframework.boot.context.properties.ConfigurationProperties;/*** Configuration properties for task scheduling.** @author Stephane Nicoll* @since 2.1.0*/
@ConfigurationProperties("spring.task.scheduling")
public class TaskSchedulingProperties {private final Pool pool = new Pool();private final Shutdown shutdown = new Shutdown();/*** Prefix to use for the names of newly created threads.*/private String threadNamePrefix = "scheduling-";public Pool getPool() {return this.pool;}public Shutdown getShutdown() {return this.shutdown;}public String getThreadNamePrefix() {return this.threadNamePrefix;}public void setThreadNamePrefix(String threadNamePrefix) {this.threadNamePrefix = threadNamePrefix;}public static class Pool {/*** Maximum allowed number of threads.*/private int size = 1;public int getSize() {return this.size;}public void setSize(int size) {this.size = size;}}public static class Shutdown {/*** Whether the executor should wait for scheduled tasks to complete on shutdown.*/private boolean awaitTermination;/*** Maximum time the executor should wait for remaining tasks to complete.*/private Duration awaitTerminationPeriod;public boolean isAwaitTermination() {return this.awaitTermination;}public void setAwaitTermination(boolean awaitTermination) {this.awaitTermination = awaitTermination;}public Duration getAwaitTerminationPeriod() {return this.awaitTerminationPeriod;}public void setAwaitTerminationPeriod(Duration awaitTerminationPeriod) {this.awaitTerminationPeriod = awaitTerminationPeriod;}}}
结论
1.spring异步线程池核心数是8,无界队列
2.如果自定义配置的bean名称为taskExecutor会覆盖默认配置
3.spring的默认定时线程池是单核心的,这个要注意,不要频繁使用太耗时的操作
