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

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

相关推荐
颜如玉3 小时前
🤲🏻🤲🏻🤲🏻临时重定向一定要能重定向🤲🏻🤲🏻🤲🏻
java·http·源码
程序员爱钓鱼3 小时前
Go语言实战案例 — 工具开发篇:实现一个图片批量压缩工具
后端·google·go
程序员的世界你不懂4 小时前
【Flask】测试平台开发,新增说明书编写和展示功能 第二十三篇
java·前端·数据库
星空寻流年4 小时前
设计模式第一章(建造者模式)
java·设计模式·建造者模式
gb42152875 小时前
java中将租户ID包装为JSQLParser的StringValue表达式对象,JSQLParser指的是?
java·开发语言·python
曾经的三心草5 小时前
Python2-工具安装使用-anaconda-jupyter-PyCharm-Matplotlib
android·java·服务器
Metaphor6925 小时前
Java 高效处理 Word 文档:查找并替换文本的全面指南
java·经验分享·word
ChinaRainbowSea5 小时前
7. LangChain4j + 记忆缓存详细说明
java·数据库·redis·后端·缓存·langchain·ai编程
舒一笑5 小时前
同步框架与底层消费机制解决方案梳理
后端·程序员
stormsha5 小时前
飞算JavaAI炫技赛电商系统商品管理模块的架构设计与实现
java·架构·鸿蒙系统