一、基本使用
1、定义一个配置类
这里一个默认的线程池,一个起了自己名字的线程池。(可以配置多个线程池)
import org.springframework.aop.interceptor.AsyncExecutionAspectSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池配置、启用异步
*
*/
@EnableAsync(proxyTargetClass = true)
@Configuration
public class AsycTaskExecutorConfig {
@Bean(name = AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME)
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程池大小
executor.setCorePoolSize(10);
//最大线程数
executor.setMaxPoolSize(20);
//队列容量
executor.setQueueCapacity(200);
//活跃时间
executor.setKeepAliveSeconds(60);
//线程名字前缀
executor.setThreadNamePrefix("taskExecutor-");
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
@Bean(name = "testEx")
public TaskExecutor testEx() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(5);
// 设置最大线程数
executor.setMaxPoolSize(10);
// 设置队列容量
executor.setQueueCapacity(20);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(60);
// 设置默认线程名称
executor.setThreadNamePrefix("test-");
// 设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
2、使用一下看看吧
@RequestMapping("/testAnys")
public void testAnys(){
System.out.println("testAnys");
testAnysMethod();
System.out.println("1");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("2");
testAnysMethod2();
System.out.println("3");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Async//这个使用默认的线程池
public void testAnysMethod(){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("async");
}
@Async("testEx")//这个使用自己命名的线程池
public void testAnysMethod2(){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("testEx");
}
// 注意返回值
@Async
Future<String> returnSomething(int i) {
// this will be run asynchronously
}
3、运行
testAnys
async
1
2
testEx
3
二、拓展使用
1、使用包装TaskDecorator(拦截器)
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.task.TaskDecorator;
public class LoggingTaskDecorator implements TaskDecorator {
private static final Log logger = LogFactory.getLog(LoggingTaskDecorator.class);
@Override
public Runnable decorate(Runnable runnable) {
return () -> {
logger.debug("Before execution of " + runnable);
runnable.run();
logger.debug("After execution of " + runnable);
};
}
}
@Bean
ThreadPoolTaskExecutor decoratedTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setTaskDecorator(new LoggingTaskDecorator());
return taskExecutor;
}
org.springframework.core.task.support.CompositeTaskDecorator
可用于顺序执行多个装饰器。
2、使用AsyncConfigurer
这种方式也可以配置默认的线程池
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//获取逻辑CPU个数
int poolSize = Runtime.getRuntime().availableProcessors();
//设置核心线程数
taskExecutor.setCorePoolSize(poolSize);
//设置最大线程数
taskExecutor.setMaxPoolSize(2 * poolSize);
//线程池所使用的缓冲队列(线程池的最大线程数用完后,等待开启线程的数量)
taskExecutor.setQueueCapacity(20 * poolSize);
// 初始化线程
taskExecutor.initialize();
return taskExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
3、异常处理
// 默认情况下,只记录异常。您可以定义一个自定义AsyncUncaughtExceptionHandler通过使用AsyncConfigurer或者<task:annotation-driven/>XML元素。
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
// handle exception
}
}
4、动态参数修改
(1)代码示例
package com.demo.springbootdemo.test;
import com.demo.springbootdemo.TestController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import javax.annotation.PostConstruct;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 基于apollo动态配置的线程池
*/
@Configuration
public class ThreadPoolConfig {
private static Logger log = LoggerFactory.getLogger(ThreadPoolConfig.class);
// 线程池存储
private volatile ThreadPoolTaskExecutor EXECUTOR;
private Integer corePoolSize;
private Integer maxPoolSize;
private Integer queueCapacity;
private Integer keepAliveSeconds;
@Value("${pool.corePoolSize:10}")
public void setCorePoolSize(Integer corePoolSize) {
this.corePoolSize = corePoolSize;
ThreadPoolTaskExecutor threadPoolTaskExecutor = getThreadPoolTaskExecutor();
if (threadPoolTaskExecutor == null) {
return;
}
log.info("thread pool update corePoolSize:{},old corePoolSize:{}", corePoolSize, threadPoolTaskExecutor.getCorePoolSize());
threadPoolTaskExecutor.setCorePoolSize(corePoolSize);
}
@Value("${pool.maxPoolSize:20}")
public void setMaxPoolSize(Integer maxPoolSize) {
this.maxPoolSize = maxPoolSize;
ThreadPoolTaskExecutor threadPoolTaskExecutor = getThreadPoolTaskExecutor();
if (threadPoolTaskExecutor == null) {
return;
}
log.info("thread pool update maxPoolSize:{},old maxPoolSize:{}", maxPoolSize, threadPoolTaskExecutor.getMaxPoolSize());
threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);
}
/**
* 注意,队列大小无法动态配置,需要重建线程池
*/
@Value("${pool.queueCapacity:100}")
public void setQueueCapacity(Integer queueCapacity) {
this.queueCapacity = queueCapacity;
ThreadPoolTaskExecutor threadPoolTaskExecutor = getThreadPoolTaskExecutor();
if (threadPoolTaskExecutor == null) {
return;
}
// threadPoolTaskExecutor.setQueueCapacity(queueCapacity);
// 重建
recreateThreadPoolTaskExecutor();
}
@Value("${pool.keepAliveSeconds:60}")
public void setKeepAliveSeconds(Integer keepAliveSeconds) {
this.keepAliveSeconds = keepAliveSeconds;
ThreadPoolTaskExecutor threadPoolTaskExecutor = getThreadPoolTaskExecutor();
if (threadPoolTaskExecutor == null) {
return;
}
log.info("thread pool update keepAliveSeconds:{},old keepAliveSeconds:{}", keepAliveSeconds, threadPoolTaskExecutor.getKeepAliveSeconds());
threadPoolTaskExecutor.setKeepAliveSeconds(keepAliveSeconds);
}
private ThreadPoolTaskExecutor createThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setThreadNamePrefix("thread-pool-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
/**
* 初始化
*/
@PostConstruct
public void initThreadPoolTaskExecutor() {
EXECUTOR = createThreadPoolTaskExecutor();
}
/**
* 只能通过此方法获取线程池
*/
public ThreadPoolTaskExecutor getThreadPoolTaskExecutor() {
return EXECUTOR;
}
/**
* 重建线程池,注意线程安全
*/
public void recreateThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor newThreadPoolExecutor = createThreadPoolTaskExecutor();
ThreadPoolTaskExecutor oldThreadPoolExecutor = EXECUTOR;
EXECUTOR = newThreadPoolExecutor;
log.info("thread pool recreate success,new thread pool:{}", newThreadPoolExecutor);
// 关闭旧线程池(温和关闭,等待任务完成)
if (oldThreadPoolExecutor != null) {
oldThreadPoolExecutor.shutdown();
}
}
}
(2)注意事项
1、最大线程数必须大于或等于核心线程数
2、支持动态修改的参数:corePoolSize、maxPoolSize、keepAliveSeconds 可通过setter方法动态修改,立即生效。
3、不支持动态修改的参数:queueCapacity(队列容量)、threadNamePrefix(线程名称前缀)等需要重建线程池才能生效,通常需重启应用。
4、配置中心集成:若使用 Nacos/Apollo,修改配置后会自动触发RefreshEvent,无需额外操作。
5、线程安全:ThreadPoolTaskExecutor的setter方法是线程安全的,可放心在运行时调用。
(3)支持动态修改的核心参数
这些参数修改后会立即生效,无需重启线程池:
-
核心线程数(corePoolSize)
- 通过
setCorePoolSize(int)
方法修改。 - 作用:调整线程池长期维持的核心线程数量。
- 注意:若当前线程数超过新的核心线程数,多余的空闲核心线程会在下次检查时被销毁(受
keepAliveTime
影响)。
- 通过
-
最大线程数(maxPoolSize)
- 通过
setMaxPoolSize(int)
方法修改。 - 作用:调整线程池允许创建的最大线程数量。
- 注意:若新值小于当前线程数,超出的线程会在空闲时被销毁,但不会强制终止正在执行的任务。
- 通过
-
空闲线程存活时间(keepAliveTime)
- 通过
setKeepAliveSeconds(int)
方法修改(ThreadPoolTaskExecutor
提供的便捷方法,底层对应ThreadPoolExecutor
的setKeepAliveTime(long, TimeUnit)
)。 - 作用:调整非核心线程(若
allowCoreThreadTimeOut
为true
,则包括核心线程)的空闲存活时间。 - 注意:修改后对新创建的线程和现有空闲线程均生效。
- 通过
-
核心线程是否允许超时(allowCoreThreadTimeOut)
- 通过
setAllowCoreThreadTimeOut(boolean)
方法修改。 - 作用:设置核心线程在空闲时是否遵循
keepAliveTime
超时策略(默认false
,核心线程永不超时)。 - 注意:修改为
true
后,核心线程会在空闲超时后被销毁,可能导致下次任务需要重新创建线程。
- 通过
-
拒绝策略(rejectedExecutionHandler)
- 通过
setRejectedExecutionHandler(RejectedExecutionHandler)
方法修改。 - 作用:调整线程池饱和时(线程满 + 队列满)的任务拒绝策略(如丢弃、抛异常、由提交者执行等)。
- 注意:修改后对新提交的任务立即生效,不影响已在队列中等待的任务。
- 通过
(4)不支持动态修改的参数
这些参数一旦线程池初始化后无法修改,若需调整必须销毁旧线程池并重建:
-
任务队列(queueCapacity)
- 队列容量由初始化时的队列实现(如
LinkedBlockingQueue
)决定,没有 setter 方法修改容量。 - 原因:JDK 阻塞队列的容量通常是固定的(或需特殊实现),且线程池内部依赖队列的状态管理,动态修改可能导致并发问题。
- 队列容量由初始化时的队列实现(如
-
线程名称前缀(threadNamePrefix)
- 线程名称前缀在创建线程工厂时确定,线程创建后名称不可修改。
- 原因:线程名称是线程的标识属性,一旦线程启动,名称无法变更,新线程会使用新前缀,但旧线程名称保持不变。
-
线程工厂(threadFactory)
- 线程工厂用于创建新线程,没有 setter 方法修改,且修改后对已创建的线程无影响。
-
是否等待任务完成后关闭(waitForTasksToCompleteOnShutdown)
- 属于线程池关闭时的行为参数,虽然有 setter 方法,但通常在初始化时确定,运行时修改意义不大。
-
关闭前的最大等待时间(awaitTerminationMillis)
- 同上述关闭行为参数,运行时修改对已启动的关闭流程无影响。
三、源码分析
springboot提供了自动配置,默认核心线程是8个,队列容量和最大线程数为 Integer.MAX_VALUE
,线程空闲存活时间为 60 秒,并且允许核心线程超时。: