SheepDog1998

博客园 首页 新随笔 联系 订阅 管理

生产级 Spring 线程池配置类详解:功能、亮点与核心知识点

 
本文将逐模块解析这份生产级线程池配置类的每行代码功能设计亮点及背后的核心知识点,帮助开发者理解从线程池初始化到监控、优雅关闭的全流程设计思路。
 

一、类整体定位与核心能力

@Slf4j
@Configuration
public class ThreadPoolConfig {
    // 核心能力:
    // 1. 基于Spring配置外部化的线程池初始化(支持默认值+参数校验)
    // 2. 线程池全维度监控(基础指标、线程状态、死锁检测)
    // 3. 优雅关闭(业务线程池+监控线程池双保障)
    // 4. 生产级鲁棒性设计(防内存泄漏、异常处理、日志结构化)
}

核心注解说明

注解 功能与知识点
@Slf4j Lombok 注解,自动生成日志对象log,简化日志打印(底层基于 SLF4J+Logback/Log4j2)
@Configuration Spring 核心注解,标识该类为配置类,会被 Spring 容器扫描并初始化,其中@Bean方法会生成 Bean 实例

 

二、配置参数定义:外部化 + 默认值兜底

// 修复:默认值改为0,通过代码计算(避免Spring EL表达式算术运算错误)
@Value("${threads.coreSize:0}")
private Integer coreSize;
@Value("${threads.maxPoolSize:0}")
private Integer maxPoolSize;
@Value("${threads.queue:500}")
private Integer queue;
@Value("${threads.keepTime:120}")
private Integer keepTime;

// 监控线程池+业务线程池引用(统一生命周期管理)
private ScheduledExecutorService monitorExecutor;
private ThreadPoolTaskExecutor eqExecutor;
 

代码功能与知识点

  1. @Value注解:配置外部化
     
    • 功能:从配置文件(如application.yml)读取threads.xxx参数,:0/:500为默认值(参数未配置时生效);
    • 亮点:默认值设为0而非直接计算(如${runtime.availableProcessors} * 2),规避 Spring EL 表达式不支持算术运算的语法陷阱;
    • 知识点:Spring EL 表达式仅支持简单变量引用(如${runtime.availableProcessors}),不支持+/*等运算,复杂默认值需代码层面计算。
     
  2. 成员变量设计
     
    • monitorExecutor:持有监控线程池引用,用于后续优雅关闭 + 置空操作,防止内存泄漏;
    • eqExecutor:持有业务线程池引用,用于监控方法中获取线程池状态,避免重复初始化。

 

三、核心方法 1:业务线程池初始化(taskExecutor()

@Bean(name = "eqExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
    // 步骤1:参数校验+默认值计算(前置保障,避免非法配置)
    validateThreadPoolParams();

    // 步骤2:初始化Spring线程池(ThreadPoolTaskExecutor是Spring对JDK ThreadPoolExecutor的封装)
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(coreSize); // 设置核心线程数(常驻线程数)
    executor.setMaxPoolSize(maxPoolSize); // 设置最大线程数(核心线程+临时线程)
    executor.setQueueCapacity(queue); // 设置任务队列容量(阻塞队列,核心线程满时存放任务)
    executor.setKeepAliveSeconds(keepTime); // 设置临时线程空闲超时时间(秒)
    executor.setThreadNamePrefix("EqTask-"); // 设置线程名前缀(便于日志/线程栈排查问题)
    // 拒绝策略:任务队列满+最大线程数满时,由提交任务的主线程执行(避免核心任务丢失)
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    // 优雅关闭配置1:等待所有提交的任务执行完成(包括队列中的任务)
    executor.setWaitForTasksToCompleteOnShutdown(true);
    // 优雅关闭配置2:等待任务完成的超时时间(60秒),超时后强制关闭
    executor.setAwaitTerminationSeconds(60);
    executor.initialize(); // 初始化线程池(必须调用,否则线程池未启动)

    // 步骤3:保存业务线程池引用,用于监控和关闭
    this.eqExecutor = executor;

    // 步骤4:初始化监控线程池
    initThreadPoolMonitor();

    // 步骤5:打印初始化日志(结构化,便于运维排查)
    log.info("EqTask线程池初始化完成,配置参数:核心线程数={}, 最大线程数={}, 队列容量={}, 空闲超时={}秒",
            coreSize, maxPoolSize, queue, keepTime);
    return executor;
}
 

核心知识点与设计亮点

代码片段 知识点与亮点
@Bean(name = "eqExecutor") 1. 生成名为eqExecutor的线程池 Bean,可通过@Autowired @Qualifier("eqExecutor")注入使用;
 
2. Spring 的ThreadPoolTaskExecutor是对 JDK ThreadPoolExecutor的封装,更适配 Spring 生态
setThreadNamePrefix("EqTask-") 线程名前缀设计:生成的线程名为EqTask-1/EqTask-2,便于通过jstack/ 日志定位业务线程
CallerRunsPolicy 拒绝策略选择:核心业务场景首选,避免任务丢失(区别于AbortPolicy(抛异常)、DiscardPolicy(丢弃任务))
setWaitForTasksToCompleteOnShutdown(true) Spring 线程池特有配置:默认关闭策略是 “立即关闭”,此配置保证应用关闭时不丢弃队列中的任务
executor.initialize() 必须调用:ThreadPoolTaskExecutor不会自动初始化,需手动调用该方法启动线程池
 

四、核心方法 2:参数校验 + 默认值计算(validateThreadPoolParams()

private void validateThreadPoolParams() {
    // 步骤1:基于CPU核心数计算默认值(适配不同服务器配置)
    int cpuCore = Runtime.getRuntime().availableProcessors();
    if (coreSize == 0) {
        coreSize = cpuCore; // 核心线程数默认=CPU核心数(CPU密集型任务最优配置)
    }
    if (maxPoolSize == 0) {
        maxPoolSize = cpuCore * 2; // 最大线程数默认=2*CPU核心数(平衡并发与资源消耗)
    }

    // 步骤2:参数合法性校验(前置失败,避免运行时异常)
    Assert.isTrue(coreSize > 0, "线程池核心线程数(coreSize)必须大于0");
    Assert.isTrue(maxPoolSize >= coreSize, "最大线程数(maxPoolSize)不能小于核心线程数(coreSize)");
    Assert.isTrue(queue >= 0, "队列容量(queue)不能为负数");
    Assert.isTrue(keepTime >= 0, "线程空闲时间(keepTime)不能为负数");
}
 

代码功能与知识点

  1. CPU 核心数适配
     
    • 功能:Runtime.getRuntime().availableProcessors()获取服务器 CPU 核心数,作为线程池参数默认值;
    • 知识点:CPU 密集型任务(如计算、排序)线程数 = CPU 核心数 / 2CPU 核心数,IO 密集型任务(如 DB / 网络调用)可设为 4CPU 核心数,此处默认配置适配通用场景。
     
  2. Assert断言工具
     
    • 功能:Spring 内置的断言工具,参数不满足条件时抛出IllegalArgumentException,快速暴露配置错误;
    • 亮点:前置校验而非运行时异常,降低问题排查成本;
    • 知识点:断言失败会直接终止应用启动,属于 “快速失败” 设计原则,避免应用带病运行。
     
 

五、核心方法 3:监控线程池初始化(initThreadPoolMonitor()

private void initThreadPoolMonitor() {
    // 步骤1:自定义ThreadFactory(规范监控线程配置)
    ThreadFactory monitorThreadFactory = r -> {
        Thread thread = new Thread(r, "EqTask-Monitor"); // 监控线程命名(便于识别)
        thread.setDaemon(true); // 设置为守护线程(JVM退出时自动终止,不阻塞应用关闭)
        thread.setPriority(Thread.MIN_PRIORITY); // 设置最低优先级(不抢占业务线程CPU资源)
        return thread;
    };

    // 步骤2:初始化单线程定时任务池(专门用于监控)
    monitorExecutor = Executors.newSingleThreadScheduledExecutor(monitorThreadFactory);
    // 步骤3:配置定时监控任务
    monitorExecutor.scheduleAtFixedRate(
        this::monitorThreadPoolStatus, // 要执行的监控方法(方法引用语法)
        10, // 首次执行延迟10秒(避免应用启动初期日志刷屏)
        30, // 执行间隔30秒(平衡监控及时性与性能消耗)
        TimeUnit.SECONDS // 时间单位(统一说明延迟/间隔的单位)
    );

    // 步骤4:打印监控初始化日志
    log.info("EqTask线程池监控任务初始化完成,监控频率:每30秒一次");
}
 

核心知识点与设计亮点

代码片段 知识点与亮点
ThreadFactory自定义 1. 线程命名:EqTask-Monitor,便于通过jps/jstack定位监控线程;
 
2. 守护线程:setDaemon(true),即使监控线程未正常关闭,JVM 也能退出;
 
3. 最低优先级:Thread.MIN_PRIORITY(值为 1),避免监控线程抢占业务线程资源
newSingleThreadScheduledExecutor JDK 定时任务池:单线程执行定时任务,避免多线程竞争,适合监控这类低频率任务
scheduleAtFixedRate 1. 固定频率执行:以上一次任务开始时间为基准计算间隔(区别于scheduleWithFixedDelay(以完成时间为基准));
 
2. 方法引用:this::monitorThreadPoolStatus等价于() -> this.monitorThreadPoolStatus(),简化 Lambda 写法
延迟 10 秒 + 间隔 30 秒 生产级监控频率设计:避免应用启动初期日志刷屏,同时降低监控对 CPU 的消耗(10 秒 / 次→30 秒 / 次)
 

六、核心方法 4:线程池监控核心逻辑(monitorThreadPoolStatus()

 
private void monitorThreadPoolStatus() {
    try {
        // 步骤1:前置校验(业务线程池已关闭则停止监控)
        if (eqExecutor == null || eqExecutor.getThreadPoolExecutor().isShutdown()) {
            log.warn("EqTask线程池已关闭,停止监控");
            if (monitorExecutor != null) {
                monitorExecutor.shutdown(); // 关闭监控线程池
                this.monitorExecutor = null; // 置空引用(防内存泄漏+重复操作)
            }
            return;
        }

        // 步骤2:获取线程池核心指标
        ThreadPoolExecutor threadPoolExecutor = eqExecutor.getThreadPoolExecutor(); // 获取底层JDK线程池
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); // JVM线程管理MXBean(用于获取线程状态/死锁)

        // 2.1 基础指标采集
        int activeCount = eqExecutor.getActiveCount(); // 活跃线程数(正在执行任务的线程数)
        int corePoolSize = eqExecutor.getCorePoolSize(); // 核心线程数
        int maxPoolSize = eqExecutor.getMaxPoolSize(); // 最大线程数
        int queueSize = threadPoolExecutor.getQueue().size(); // 任务队列当前大小
        int queueRemainingCapacity = threadPoolExecutor.getQueue().remainingCapacity(); // 队列剩余容量
        long completedTaskCount = threadPoolExecutor.getCompletedTaskCount(); // 已完成任务数
        long totalTaskCount = threadPoolExecutor.getTaskCount(); // 总提交任务数

        // 2.2 线程状态统计(空值保护+并发安全)
        Map<Thread.State, Integer> threadStateMap = new HashMap<>();
        ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 0); // 0表示不获取堆栈(提升性能)
        if (threadInfos != null) {
            for (ThreadInfo threadInfo : threadInfos) {
                // 空值保护:避免threadInfo/threadName为空导致空指针
                if (threadInfo != null && threadInfo.getThreadName() != null
                        && threadInfo.getThreadName().startsWith("EqTask-")) {
                    Thread.State state = threadInfo.getThreadState(); // 获取线程状态(RUNNABLE/WAITING等)
                    // 统计每种状态的线程数(getOrDefault避免空指针)
                    threadStateMap.put(state, threadStateMap.getOrDefault(state, 0) + 1);
                }
            }
        }

        // 2.3 死锁检测(简化版,覆盖所有死锁类型)
        long[] deadlockThreads = threadMXBean.findDeadlockedThreads(); // 检测所有死锁(含JNI)
        boolean hasDeadlock = deadlockThreads != null && deadlockThreads.length > 0;

        // 步骤3:打印结构化监控日志(便于ELK/日志工具解析)
        log.info("=== EqTask线程池监控 ===");
        log.info("基础信息 | 活跃线程数:{} | 核心线程数:{} | 最大线程数:{}",
                activeCount, corePoolSize, maxPoolSize);
        log.info("队列信息 | 当前队列大小:{} | 剩余容量:{} | 总容量:{}",
                queueSize, queueRemainingCapacity, queue);
        log.info("任务信息 | 已完成任务数:{} | 总任务数:{}",
                completedTaskCount, totalTaskCount);
        log.info("线程状态 | {}", Collections.unmodifiableMap(threadStateMap)); // 不可修改Map(并发安全)
        log.info("死锁检测 | {}", hasDeadlock ? "⚠️ 存在死锁!" : "✅ 无死锁");
        log.info("=======================");

        // 步骤4:死锁详情打印(仅在检测到死锁时,减少性能消耗)
        if (hasDeadlock) {
            log.error("=== 死锁详情 ===");
            printDeadlockDetails(threadMXBean, deadlockThreads, "死锁");
            log.error("=================");
        }

    } catch (Exception e) {
        // 异常捕获:避免监控任务异常终止(生产级必备)
        log.error("EqTask线程池监控任务执行异常,将继续执行下一次监控", e);
    }
}
 

核心知识点与设计亮点

  1. JVM 线程管理 API(ThreadMXBean
     
    • 功能:ManagementFactory.getThreadMXBean()获取 JVM 线程管理 Bean,用于获取线程状态、检测死锁;
    • 知识点:
      • findDeadlockedThreads():检测所有死锁(包括 Java 级和 JNI 级),替代冗余的findMonitorDeadlockedThreads()(仅检测 Java 级死锁);
      • getThreadInfo(threadIds, 0)0表示不获取线程堆栈,日常监控提升性能,仅在死锁时获取完整堆栈。
       
     
  2. 线程状态统计
     
    • 线程状态枚举(Thread.State):NEW(新建)、RUNNABLE(运行 / 就绪)、BLOCKED(阻塞)、WAITING(无时限等待)、TIMED_WAITING(有时限等待)、TERMINATED(终止);
    • 亮点:空值保护(threadInfo != null)+ 线程名过滤(startsWith("EqTask-")),仅统计业务线程池的线程状态。
     
  3. 日志设计亮点
     
    • 结构化:使用===分隔监控块,指标分类打印(基础 / 队列 / 任务 / 线程状态),便于运维快速定位;
    • 并发安全:Collections.unmodifiableMap(threadStateMap)包装 Map,避免并发修改异常;
    • 性能优化:仅在死锁时打印堆栈,日常监控不获取堆栈,减少 CPU / 内存消耗。
     
  4. 异常处理
     
    • 亮点:try-catch捕获所有异常,避免监控任务因单次异常终止,保证监控连续性;
    • 知识点:定时任务池的任务若抛出未捕获异常,会导致任务终止,生产级必须捕获所有异常。
     

七、辅助方法:死锁详情打印(printDeadlockDetails()

private void printDeadlockDetails(ThreadMXBean threadMXBean, long[] deadlockThreadIds, String deadlockType) {
    // 空值保护
    if (deadlockThreadIds == null || deadlockThreadIds.length == 0) {
        return;
    }
    // 打印死锁线程数
    log.error("{}:共{}个线程", deadlockType, deadlockThreadIds.length);
    // 遍历死锁线程,打印ID、名称、状态、完整堆栈
    for (long threadId : deadlockThreadIds) {
        // 获取完整堆栈(Integer.MAX_VALUE表示获取全部堆栈帧)
        ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId, Integer.MAX_VALUE);
        if (threadInfo != null) {
            log.error("线程ID:{} | 线程名:{} | 状态:{}",
                    threadId, threadInfo.getThreadName(), threadInfo.getThreadState());
            log.error("堆栈信息:\n{}", Arrays.toString(threadInfo.getStackTrace()));
        }
    }
}
 

核心知识点

  • getThreadInfo(threadId, Integer.MAX_VALUE):获取死锁线程的完整堆栈,便于定位死锁代码位置;
  • 死锁排查关键:通过线程堆栈可分析线程持有的锁和等待的锁,定位死锁源头。
 

八、核心方法 5:优雅关闭所有线程池(destroyAllExecutors()

@PreDestroy
public void destroyAllExecutors() {
    log.info("开始关闭EqTask线程池及监控任务...");

    // 步骤1:关闭监控线程池(优雅+强制双保障)
    if (monitorExecutor != null && !monitorExecutor.isShutdown()) {
        monitorExecutor.shutdown(); // 优雅关闭:拒绝新任务,等待已提交任务完成
        try {
            // 等待5秒,超时则强制关闭
            if (!monitorExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
                monitorExecutor.shutdownNow(); // 强制关闭:中断正在执行的任务
                log.warn("监控线程池等待超时,已强制关闭");
            } else {
                log.info("监控线程池已正常关闭");
            }
        } catch (InterruptedException e) {
            // 中断异常处理:强制关闭+恢复中断状态(线程中断最佳实践)
            monitorExecutor.shutdownNow();
            Thread.currentThread().interrupt(); // 恢复中断状态,不吞掉中断信号
            log.error("监控线程池关闭被中断,已强制关闭", e);
        }
        this.monitorExecutor = null; // 置空引用(防内存泄漏+重复操作)
    }

    // 步骤2:校验并关闭业务线程池(Spring自动关闭+二次确认)
    if (eqExecutor != null) {
        ThreadPoolExecutor pool = eqExecutor.getThreadPoolExecutor();
        if (!pool.isShutdown()) {
            log.warn("EqTask业务线程池未正常关闭,触发手动关闭");
            pool.shutdown();
        } else if (!pool.isTerminated()) {
            // 区分“已关闭”和“已终止”状态,避免误判
            log.info("EqTask业务线程池已关闭但未完全终止,等待剩余任务完成");
        }
    }

    log.info("EqTask线程池及监控任务关闭完成");
}
 

核心知识点与设计亮点

代码片段 知识点与亮点
@PreDestroy Spring 注解,容器销毁该 Bean 前执行,保证应用关闭时触发线程池关闭
shutdown() + awaitTermination() 优雅关闭组合:shutdown()发起关闭,awaitTermination()阻塞等待超时,避免无限等待
shutdownNow() 强制关闭:中断正在执行的任务(通过Thread.interrupt()),返回未执行的任务列表
Thread.currentThread().interrupt() 线程中断最佳实践:捕获InterruptedException后恢复中断状态,避免上层代码无法感知中断
monitorExecutor = null 置空引用:关闭线程池后释放引用,防止内存泄漏 + 重复关闭操作
业务线程池状态校验 区分isShutdown()(已关闭,拒绝新任务)和isTerminated()(已终止,所有任务完成),避免误判
 

九、整体设计亮点总结

设计维度 核心亮点
鲁棒性 1. 参数前置校验 + 默认值兜底;
 
2. 全链路空值保护;
 
3. 异常捕获不终止监控
性能 1. 监控频率降低(30 秒 / 次);
 
2. 按需获取线程堆栈;
 
3. 监控线程低优先级
可维护性 1. 结构化日志;
 
2. 线程 / 线程池命名规范;
 
3. 方法职责单一
资源安全 1. 线程池关闭后置空引用;
 
2. 守护线程配置;
 
3. 优雅关闭 + 强制关闭双保障
生产级适配 1. 配置外部化;
 
2. 死锁检测 + 详情打印;
 
3. 线程池状态全维度监控
 

十、使用建议

  1. 配置文件补充:在application.yml中配置线程池参数(无配置则使用默认值):
     
    threads:
      coreSize: 8
      maxPoolSize: 16
      queue: 1000
      keepTime: 180
  2. 监控告警:基于日志中的 “死锁检测”“队列剩余容量” 配置告警规则(如队列剩余容量 < 100 时告警);
  3. 线程池隔离:不同业务模块使用独立线程池配置类,避免单模块故障影响全局;
  4. 压测验证:上线前通过压测调整核心线程数 / 队列容量,适配业务并发量。

十一、源码

package com.push.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.Assert;

import javax.annotation.PreDestroy;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 线程池配置类
 * @Description: 配置带监控功能的业务线程池,支持外部化配置、线程状态监控、死锁检测,且能优雅关闭所有线程池
 * @Author:
 * @CreateTime: 2024-11-13 16:39
 * @Version: 1.0
 */
@Slf4j
@Configuration
public class ThreadPoolConfig {

    // 修复:默认值改为0,通过代码计算
    @Value("${threads.coreSize:0}")
    private Integer coreSize;
    @Value("${threads.maxPoolSize:0}")
    private Integer maxPoolSize;
    @Value("${threads.queue:500}")
    private Integer queue;
    @Value("${threads.keepTime:120}")
    private Integer keepTime;

    private ScheduledExecutorService monitorExecutor;
    private ThreadPoolTaskExecutor eqExecutor;

    @Bean(name = "eqExecutor")
    public ThreadPoolTaskExecutor taskExecutor() {
        validateThreadPoolParams();

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(coreSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queue);
        executor.setKeepAliveSeconds(keepTime);
        executor.setThreadNamePrefix("EqTask-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.initialize();

        this.eqExecutor = executor;
        initThreadPoolMonitor();

        log.info("EqTask线程池初始化完成,配置参数:核心线程数={}, 最大线程数={}, 队列容量={}, 空闲超时={}秒",
                coreSize, maxPoolSize, queue, keepTime);
        return executor;
    }

    private void validateThreadPoolParams() {
        // 先计算CPU核心数,设置默认值
        int cpuCore = Runtime.getRuntime().availableProcessors();
        if (coreSize == 0) {
            coreSize = cpuCore;
        }
        if (maxPoolSize == 0) {
            maxPoolSize = cpuCore * 2;
        }

        // 再校验参数合法性
        Assert.isTrue(coreSize > 0, "线程池核心线程数(coreSize)必须大于0");
        Assert.isTrue(maxPoolSize >= coreSize, "最大线程数(maxPoolSize)不能小于核心线程数(coreSize)");
        Assert.isTrue(queue >= 0, "队列容量(queue)不能为负数");
        Assert.isTrue(keepTime >= 0, "线程空闲时间(keepTime)不能为负数");
    }

    private void initThreadPoolMonitor() {
        // 规范的ThreadFactory写法
        ThreadFactory monitorThreadFactory = r -> {
            Thread thread = new Thread(r, "EqTask-Monitor");
            thread.setDaemon(true);
            thread.setPriority(Thread.MIN_PRIORITY);
            return thread;
        };

        monitorExecutor = Executors.newSingleThreadScheduledExecutor(monitorThreadFactory);
        monitorExecutor.scheduleAtFixedRate(this::monitorThreadPoolStatus, 10, 30, TimeUnit.SECONDS);
        log.info("EqTask线程池监控任务初始化完成,监控频率:每30秒一次");
    }

    private void monitorThreadPoolStatus() {
        try {
            if (eqExecutor == null || eqExecutor.getThreadPoolExecutor().isShutdown()) {
                log.warn("EqTask线程池已关闭,停止监控");
                if (monitorExecutor != null) {
                    monitorExecutor.shutdown();
                    this.monitorExecutor = null; // 新增:置空,避免重复操作
                }
                return;
            }

            ThreadPoolExecutor threadPoolExecutor = eqExecutor.getThreadPoolExecutor();
            ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();

            // 基础信息
            int activeCount = eqExecutor.getActiveCount();
            int corePoolSize = eqExecutor.getCorePoolSize();
            int maxPoolSize = eqExecutor.getMaxPoolSize();
            int queueSize = threadPoolExecutor.getQueue().size();
            int queueRemainingCapacity = threadPoolExecutor.getQueue().remainingCapacity();
            long completedTaskCount = threadPoolExecutor.getCompletedTaskCount();
            long totalTaskCount = threadPoolExecutor.getTaskCount();

            // 线程状态统计
            Map<Thread.State, Integer> threadStateMap = new HashMap<>();
            ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 0);
            if (threadInfos != null) {
                for (ThreadInfo threadInfo : threadInfos) {
                    if (threadInfo != null && threadInfo.getThreadName() != null
                            && threadInfo.getThreadName().startsWith("EqTask-")) {
                        Thread.State state = threadInfo.getThreadState();
                        threadStateMap.put(state, threadStateMap.getOrDefault(state, 0) + 1);
                    }
                }
            }

            // 简化死锁检测
            long[] deadlockThreads = threadMXBean.findDeadlockedThreads();
            boolean hasDeadlock = deadlockThreads != null && deadlockThreads.length > 0;

            // 打印日志(移除多余空格)
            log.info("=== EqTask线程池监控 ===");
            log.info("基础信息 | 活跃线程数:{} | 核心线程数:{} | 最大线程数:{}",
                    activeCount, corePoolSize, maxPoolSize);
            log.info("队列信息 | 当前队列大小:{} | 剩余容量:{} | 总容量:{}",
                    queueSize, queueRemainingCapacity, queue);
            log.info("任务信息 | 已完成任务数:{} | 总任务数:{}",
                    completedTaskCount, totalTaskCount);
            log.info("线程状态 | {}", Collections.unmodifiableMap(threadStateMap));
            log.info("死锁检测 | {}", hasDeadlock ? "⚠️ 存在死锁!" : "✅ 无死锁");
            log.info("=======================");

            // 死锁详情
            if (hasDeadlock) {
                log.error("=== 死锁详情 ===");
                printDeadlockDetails(threadMXBean, deadlockThreads, "死锁");
                log.error("=================");
            }

        } catch (Exception e) {
            log.error("EqTask线程池监控任务执行异常,将继续执行下一次监控", e);
        }
    }

    private void printDeadlockDetails(ThreadMXBean threadMXBean, long[] deadlockThreadIds, String deadlockType) {
        if (deadlockThreadIds == null || deadlockThreadIds.length == 0) {
            return;
        }
        log.error("{}:共{}个线程", deadlockType, deadlockThreadIds.length);
        for (long threadId : deadlockThreadIds) {
            ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId, Integer.MAX_VALUE);
            if (threadInfo != null) {
                log.error("线程ID:{} | 线程名:{} | 状态:{}",
                        threadId, threadInfo.getThreadName(), threadInfo.getThreadState());
                log.error("堆栈信息:\n{}", Arrays.toString(threadInfo.getStackTrace()));
            }
        }
    }

    @PreDestroy
    public void destroyAllExecutors() {
        log.info("开始关闭EqTask线程池及监控任务...");

        // 关闭监控线程池
        if (monitorExecutor != null && !monitorExecutor.isShutdown()) {
            monitorExecutor.shutdown();
            try {
                if (!monitorExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
                    monitorExecutor.shutdownNow();
                    log.warn("监控线程池等待超时,已强制关闭");
                } else {
                    log.info("监控线程池已正常关闭");
                }
            } catch (InterruptedException e) {
                monitorExecutor.shutdownNow();
                Thread.currentThread().interrupt();
                log.error("监控线程池关闭被中断,已强制关闭", e);
            }
            this.monitorExecutor = null; // 置空
        }

        // 检查业务线程池
        if (eqExecutor != null) {
            ThreadPoolExecutor pool = eqExecutor.getThreadPoolExecutor();
            if (!pool.isShutdown()) {
                log.warn("EqTask业务线程池未正常关闭,触发手动关闭");
                pool.shutdown();
            } else if (!pool.isTerminated()) {
                log.info("EqTask业务线程池已关闭但未完全终止,等待剩余任务完成");
            }
        }

        log.info("EqTask线程池及监控任务关闭完成");
    }
}



posted on 2026-01-29 15:40  SheepDog1998  阅读(5)  评论(0)    收藏  举报