SpringBoot优雅使用线程池
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
配置文件
task.pool.enabled=true
task.pool.corePoolSize=20
task.pool.maxPoolSize=40
task.pool.keepAliveSeconds=300
task.pool.queueCapacity=-1
task.pool.threadNamePrefix=Execute-
task.pool.threadGroupName=Group-
配置文件Bean
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;
@Data
@ConfigurationProperties(prefix = "task.pool")
public class TaskThreadPoolProperties {
/**
* 线程池是否可用
*/
private boolean enabled = true;
/**
* 核心线程数
*/
private int corePoolSize = 20;
/**
* 最大线程数
*/
private int maxPoolSize = 40;
/**
* 线程池维护线程所允许的空闲时间
*/
private int keepAliveSeconds = 300;
/**
* 队列最大长度
*/
private int queueCapacity = -1;
/**
* 线程名前缀
*/
private String threadNamePrefix = "Execute-";
/**
* 线程组名称
*/
private String threadGroupName = "Group-";
}
线程池配置
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@EnableAsync(proxyTargetClass = true)
@Configuration
@EnableConfigurationProperties({TaskThreadPoolProperties.class}) // 携带注入TaskThreadPoolProperties
@ConditionalOnProperty(prefix = "task.pool", value = "enabled", matchIfMissing = true) // 配置enabled为true才注入bean
public class TaskThreadPoolConfig {
@Autowired
private TaskThreadPoolProperties properties;
// 定义线程池
@Bean
public AsyncTaskExecutor taskThreadPool() {
// 不做扩展直接使用ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 可跳过自定义扩展线程池
// 线程池扩展VisiableThreadPoolTaskExecutor
ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
// 核心线程池大小
executor.setCorePoolSize(properties.getCorePoolSize());
// 最大线程数
executor.setMaxPoolSize(properties.getMaxPoolSize());
// 队列容量
executor.setQueueCapacity(properties.getQueueCapacity());
// 活跃时间
executor.setKeepAliveSeconds(properties.getKeepAliveSeconds());
// 线程名字前缀
executor.setThreadNamePrefix(properties.getThreadNamePrefix());
// 线程组名称
executor.setThreadGroupName(properties.getThreadGroupName());
/*
* setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务,JDK提供了几个策略,这里使用CallerRunsPolicy
* CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
*/
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
自定义扩展线程池
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.concurrent.ListenableFuture;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
@Slf4j
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private static final long serialVersionUID = -8735033356653081039L;
// 多线程任务装饰器
public VisiableThreadPoolTaskExecutor(){
// 这里对线程任务执行前后进行包装
setTaskDecorator(new MDCTaskDecorator());
}
// 线程执行任务过程时打印线程池日志
private void showThreadPoolInfo(String prefix) {
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
if (null == threadPoolExecutor) {
return;
}
log.info( "{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
this.getThreadNamePrefix(), prefix, threadPoolExecutor.getTaskCount(),
threadPoolExecutor.getCompletedTaskCount(), threadPoolExecutor.getActiveCount(),
threadPoolExecutor.getQueue().size());
}
@Override
public void execute(Runnable task) {
showThreadPoolInfo("1. do execute(Runnable)");
super.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
showThreadPoolInfo("2. do execute(Runnable, long)");
super.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
showThreadPoolInfo("1. do submit(Runnable)");
return super.submit(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
showThreadPoolInfo("2. do submit(Callable<T>)");
return super.submit(task);
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
showThreadPoolInfo("1. do submitListenable(Runnable)");
return super.submitListenable(task);
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
showThreadPoolInfo("2. do submitListenable(Callable<T>)");
return super.submitListenable(task);
}
}
MDCTaskDecorator,对执行的工作线程任务进行包装,这里场景是初始化每个线程独有的变量上下文,原理是使用MDC的ThreadLocal
import org.apache.commons.collections4.MapUtils;
import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;
import java.util.Map;
public class MDCTaskDecorator implements TaskDecorator {
// 包装完的线程任务
@Override
public Runnable decorate(Runnable runnable) {
// web thread, 每个工作线程都单独copy一份上下文数据
Map<String, String> contextMap = ContextUtil.getCopyOfContextMap();
return () -> {
// work thread
try {
if(MapUtils.isNotEmpty(contextMap)){
MDC.setContextMap(contextMap);
}
// 具体的业务执行
runnable.run();
} finally {
MDC.clear();
}
};
}
}
应用上下文工具类ContextUtil
import org.slf4j.MDC;
import com.alibaba.fastjson.JSONObject;
import java.util.Map;
/**
* Description:上下文信息
*/
public class ContextUtil {
private ContextUtil() {}
public static final String USER_ID = "userId";
public static final String CLIENT_IP = "clientIp";
public static final String ACCESS_TOKEN = "accessToken";
public static final String SYS_LANGUAGE = "sysLanguage";
// 上下文json串
public static final String CONTEXT = "relayContext";
public static void setUserId(String userId) {
MDC.put(USER_ID, userId);
}
public static void setClientIp(String clientIp) {
MDC.put(CLIENT_IP, clientIp);
}
public static void setAccessToken(String accessToken) {
MDC.put(ACCESS_TOKEN, accessToken);
}
public static void setSysLanguage(String sysLanguage) {MDC.put(SYS_LANGUAGE, sysLanguage);}
public static void initContext(String context) {
RelayContext vo = JSONObject.parseObject(context, RelayContext.class);
initContext(vo);
}
public static void initContext(RelayContext vo) {
MDC.put(USER_ID, vo.getUserId());
MDC.put(CLIENT_IP, vo.getClientIp());
MDC.put(ACCESS_TOKEN, vo.getAccessToken());
MDC.put(SYS_LANGUAGE, vo.getSysLanguage());
MDC.put(CONTEXT, JSONObject.toJSONString(vo));
}
public static String getUserId() {
return MDC.get(USER_ID);
}
public static String getClientIp() {
return MDC.get(CLIENT_IP);
}
public static String getAccessToken() {
return MDC.get(ACCESS_TOKEN);
}
public static String getSysLanguage(){
return MDC.get(SYS_LANGUAGE);
}
public static String getContext() {
return MDC.get(CONTEXT);
}
public static void resetContext() {
RelayContext vo = new RelayContext();
MDC.put(USER_ID, vo.getUserId());
MDC.put(CLIENT_IP, vo.getClientIp());
MDC.put(ACCESS_TOKEN, vo.getAccessToken());
MDC.put(SYS_LANGUAGE, vo.getSysLanguage());
MDC.put(CONTEXT, JSONObject.toJSONString(vo));
}
public static void clear() {
MDC.clear();
}
public static Map<String,String> getCopyOfContextMap(){
return MDC.getCopyOfContextMap();
}
}
使用线程池
// 外部注入TaskSerivce,调用asyncInsertTable方法即可使用线程池异步处理
@Service
@Slf4j
public class TaskSerivceImpl implements TaskSerivce {
@Override
@Async("taskThreadPool")
public void asyncInsertTable() {
// do somethings
}
}
// 注入线程池使用
@Autowired
@Qualifier("taskThreadPool")
private AsyncTaskExecutor executor;
executor.submit(() -> {
// 逻辑
});
源码
看看SpringBoot提供的线程池部分源码Api
装饰器接口:
@FunctionalInterface
public interface TaskDecorator {
Runnable decorate(Runnable runnable);
}
线程池Executor:
public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {
// 装饰器增加线程任务
@Nullable
private TaskDecorator taskDecorator;
@Nullable
private ThreadPoolExecutor threadPoolExecutor;
// Runnable decorator to user-level FutureTask, if different
private final Map<Runnable, Object> decoratedTaskMap =
new ConcurrentReferenceHashMap<>(16, ConcurrentReferenceHashMap.ReferenceType.WEAK);
// 注入
public void setTaskDecorator(TaskDecorator taskDecorator) {
this.taskDecorator = taskDecorator;
}
// 线程初初始化
@Override
protected ExecutorService initializeExecutor(
ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);
ThreadPoolExecutor executor;
if (this.taskDecorator != null) {
executor = new ThreadPoolExecutor(
this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
queue, threadFactory, rejectedExecutionHandler) {
@Override
public void execute(Runnable command) {
// 包装工作线程,返回增加后的线程
Runnable decorated = taskDecorator.decorate(command);
if (decorated != command) {
decoratedTaskMap.put(decorated, command);
}
super.execute(decorated);
}
};
}
else {
executor = new ThreadPoolExecutor(
this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
queue, threadFactory, rejectedExecutionHandler);
}
if (this.allowCoreThreadTimeOut) {
executor.allowCoreThreadTimeOut(true);
}
this.threadPoolExecutor = executor;
return executor;
}
@Override
protected void cancelRemainingTask(Runnable task) {
super.cancelRemainingTask(task);
// Cancel associated user-level Future handle as well
Object original = this.decoratedTaskMap.get(task);
if (original instanceof Future) {
((Future<?>) original).cancel(true);
}
}
}