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

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 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp5 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑5 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯6 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan8 小时前
多Agent之间的区别
后端
青石路10 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充11 小时前
1.面向对象设计思想
后端
IT_陈寒11 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro11 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗12 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端