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

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 分钟前
UNIX下C语言编程与实践16-UNIX 磁盘空间划分:引导块、超级块、i 节点区、数据区的功能解析
java·c语言·unix
MetetorShower15 分钟前
深入解析MCP:从Function Calling到工具调用的标准化革命
后端
Emrys_22 分钟前
装饰者模式详解与计费功能实现
后端
猎豹奕叔26 分钟前
注解规则编排组件
后端
程序员小假27 分钟前
线程池执行过程中遇到异常该怎么办?
java·后端
karry_k27 分钟前
常用的同步辅助类
后端
稚辉君.MCA_P8_Java32 分钟前
DeepSeek Java 单例模式详解
java·spring boot·微服务·单例模式·kubernetes
洛_尘40 分钟前
数据结构--4:栈和队列
java·数据结构·算法
疯癫的老码农1 小时前
【小白入门docker】创建Spring Boot Hello World应用制作Docker镜像并运行
java·spring boot·分布式·docker·微服务
Mr.Entropy1 小时前
Hibernate批量查询方法全面解析
java·后端·hibernate