创建一个生产可用的线程池

1、背景

近期需求开发过程中,有些查询希望进行异步整合,以及一些操作也希望异步化,所以使用了多线程进行优化,翻看代码发现大部分需要多线程操作的地方都是由开发自己维护了一个线程池,东一榔头西一棒槌的,不统一也不便于维护,于是想维护一些线程池,既达到目的,也方便进行维护,同时进行线程池资源隔离,防止线程耗尽阻塞整个服务。

2、异步线程池配置

Java 复制代码
@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig {

    private static final String TRACE_ID = "traceId";

    private static final String DEFAULT_THREAD_NAME_PREFIX = "DEFAULT_THREAD_POOL";

    @Bean("defaultExecutor")
    public Executor defaultExecutor() {
        ThreadPoolTaskExecutor executor = new TracingThreadPoolTaskExecutor();

        int availableProcessors = Runtime.getRuntime().availableProcessors();
        // 核心线程数
        executor.setCorePoolSize(availableProcessors);

        // 最大线程数
        executor.setMaxPoolSize(availableProcessors * 2);

        // 等待队列大小
        executor.setQueueCapacity(availableProcessors * 20);

        // 线程存活时间(秒)
        executor.setKeepAliveSeconds(60);

        // 线程名前缀
        executor.setThreadNamePrefix(DEFAULT_THREAD_NAME_PREFIX);

        // 设置拒绝策略
        // 可选策略:
        // - AbortPolicy: 抛出异常(默认)
        // - CallerRunsPolicy: 由调用线程执行任务(防止系统崩溃,但会降低响应)
        // - DiscardPolicy: 直接丢弃任务
        // - DiscardOldestPolicy: 丢弃队列中最老的任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        // 初始化线程池
        executor.initialize();

        return executor;
    }

    private static class TracingThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {

        @Override
        public void execute(@NotNull Runnable task) {
            super.execute(wrap(task, getTraceContext()));
        }

        @NotNull
        @Override
        public <T> Future<T> submit(@NotNull Callable<T> task) {
            return super.submit(wrap(task, getTraceContext()));
        }

        @NotNull
        @Override
        public Future<?> submit(@NotNull Runnable task) {
            return super.submit(wrap(task, getTraceContext()));
        }

        private Runnable wrap(Runnable runnable, Map<String, String> context) {
            return () -> {
                // 保存原始子线程 MDC上下文
                Map<String, String> original = MDC.getCopyOfContextMap();
                // 父线程存在MDC上下文 则使用父线程 否则由子线程新建
                if (MapUtils.isNotEmpty(context)) {
                    MDC.setContextMap(context);
                } else {
                    Map<String, String> contextMap = buildNewContext();
                    MDC.setContextMap(contextMap);
                }
                try {
                    runnable.run();
                } catch (Exception e) {
                    log.error("Runnable执行错误", e);
                } finally {
                    if (MapUtils.isNotEmpty(original)) {
                        MDC.setContextMap(original);
                    } else {
                        MDC.clear();
                    }
                }
            };
        }

        private <T> Callable<T> wrap(Callable<T> callable, Map<String, String> context) {
            return () -> {
                // 保存原始子线程 MDC上下文
                Map<String, String> original = MDC.getCopyOfContextMap();
                // 父线程存在MDC上下文 则使用父线程 否则由子线程新建
                if (MapUtils.isNotEmpty(context)) {
                    MDC.setContextMap(context);
                } else {
                    Map<String, String> contextMap = buildNewContext();
                    MDC.setContextMap(contextMap);
                }
                try {
                    return callable.call();
                } catch (Exception e) {
                    log.error("Callable执行错误", e);
                    throw e;
                } finally {
                    if (MapUtils.isNotEmpty(original)) {
                        MDC.setContextMap(original);
                    } else {
                        MDC.clear();
                    }
                }
            };
        }

        private Map<String, String> getTraceContext() {
            return MDC.getCopyOfContextMap();
        }

        private Map<String, String> buildNewContext() {
            Map<String, String> contextMap = new HashMap<>();
            contextMap.put(TRACE_ID, UUID.fastUUID().toString(true));
            return contextMap;
        }
    }
}

3、解释

1、AsyncConfig

此配置是为了结合@Async注解而编写

2、defaultExecutor

此bean已被Spring所管理,用于常规异步任务,可单独由@Autowired或@Resource使用。 可以维护多个类似的bean,用于隔离多个线程池资源,例如用于异步操作数据库等的线程池。 使用@Bean注解进行定义即可。

3、TRACE_ID

基于MDC增加了TRACE_ID,处理了父子线程间TRACE_ID的传递,方便链路追踪。因为目前项目也是存在TRACE_ID的,也是做一个兼容。

4、关于此线程池的说明

目前线程池已经足以应对一般的规模,CPU密集型的任务可用,使用较少的线程避免上下文切换。如果是IO密集型的,根据规模可自行定义,可以进行压测进行调整。进一步可使用动态线程池进行动态调整优化。

相关推荐
傲文博一4 分钟前
为什么我的产品尽量不用「外置」动态链接库
前端·后端
零日失眠者6 分钟前
【系统监控系列】005:CPU温度监控脚本
后端·python
踏浪无痕8 分钟前
Maven 依赖拉不下来?一文终结所有坑点
spring boot·后端·面试
踏浪无痕10 分钟前
你真的懂泛型吗?手写 MyBatis-Plus + Jackson,揭秘框架设计的精髓
后端·json·mybatis
老青蛙11 分钟前
Easy Work-简单、易用、傻瓜式的 Java 流程引擎
java·开源
茶杯67512 分钟前
“舒欣双免“方案助力MSI-H/dMMR结肠癌治疗新突破
java·服务器·前端
随风飘的云12 分钟前
mysql的in查询列数据量非常大导致数据索引失效的解决方案
后端
我真会写代码12 分钟前
从入门到精通:Java Socket 网络编程实战(含线程池优化)
java·linux·服务器·socket·tcp/ip协议
凯哥197013 分钟前
离线使用 Docker 镜像
后端
Stream13 分钟前
大模型应用技术之Rerank重排序
后端