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

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密集型的,根据规模可自行定义,可以进行压测进行调整。进一步可使用动态线程池进行动态调整优化。

相关推荐
武子康9 分钟前
大数据-132 Flink SQL 实战入门 | 3 分钟跑通 Table API + SQL 含 toChangelogStream 新写法
大数据·后端·flink
李辰洋16 分钟前
go tools安装
开发语言·后端·golang
Code_Geo17 分钟前
agent设计模式:第三章节—并行化
java·设计模式·agent·并行化
Javatutouhouduan21 分钟前
2025Java高质量面试真题汇总!
java·高并发·java面试·java面试题·后端开发·java架构师·java八股文
wanfeng_0921 分钟前
go lang
开发语言·后端·golang
绛洞花主敏明23 分钟前
go build -tags的其他用法
开发语言·后端·golang
渣哥44 分钟前
从代理到切面:Spring AOP 的本质与应用场景解析
javascript·后端·面试
文心快码BaiduComate1 小时前
文心快码3.5S实测插件开发,Architect模式令人惊艳
前端·后端·架构
维诺菌1 小时前
k8s java应用pod内存占用过高问题排查
java·jvm·云原生·容器·性能优化·kubernetes