生产级 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;
代码功能与知识点
-
@Value注解:配置外部化
- 功能:从配置文件(如
application.yml)读取threads.xxx参数,:0/:500为默认值(参数未配置时生效);
- 亮点:默认值设为
0而非直接计算(如${runtime.availableProcessors} * 2),规避 Spring EL 表达式不支持算术运算的语法陷阱;
- 知识点:Spring EL 表达式仅支持简单变量引用(如
${runtime.availableProcessors}),不支持+/*等运算,复杂默认值需代码层面计算。
-
成员变量设计
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)不能为负数");
}
代码功能与知识点
-
CPU 核心数适配
- 功能:
Runtime.getRuntime().availableProcessors()获取服务器 CPU 核心数,作为线程池参数默认值;
- 知识点:CPU 密集型任务(如计算、排序)线程数 = CPU 核心数 / 2CPU 核心数,IO 密集型任务(如 DB / 网络调用)可设为 4CPU 核心数,此处默认配置适配通用场景。
-
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);
}
}
核心知识点与设计亮点
-
JVM 线程管理 API(ThreadMXBean)
- 功能:
ManagementFactory.getThreadMXBean()获取 JVM 线程管理 Bean,用于获取线程状态、检测死锁;
- 知识点:
findDeadlockedThreads():检测所有死锁(包括 Java 级和 JNI 级),替代冗余的findMonitorDeadlockedThreads()(仅检测 Java 级死锁);
getThreadInfo(threadIds, 0):0表示不获取线程堆栈,日常监控提升性能,仅在死锁时获取完整堆栈。
-
线程状态统计
- 线程状态枚举(
Thread.State):NEW(新建)、RUNNABLE(运行 / 就绪)、BLOCKED(阻塞)、WAITING(无时限等待)、TIMED_WAITING(有时限等待)、TERMINATED(终止);
- 亮点:空值保护(
threadInfo != null)+ 线程名过滤(startsWith("EqTask-")),仅统计业务线程池的线程状态。
-
日志设计亮点
- 结构化:使用
===分隔监控块,指标分类打印(基础 / 队列 / 任务 / 线程状态),便于运维快速定位;
- 并发安全:
Collections.unmodifiableMap(threadStateMap)包装 Map,避免并发修改异常;
- 性能优化:仅在死锁时打印堆栈,日常监控不获取堆栈,减少 CPU / 内存消耗。
-
异常处理
- 亮点:
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. 线程池状态全维度监控 |
十、使用建议
- 配置文件补充:在
application.yml中配置线程池参数(无配置则使用默认值):
threads:
coreSize: 8
maxPoolSize: 16
queue: 1000
keepTime: 180
- 监控告警:基于日志中的 “死锁检测”“队列剩余容量” 配置告警规则(如队列剩余容量 < 100 时告警);
- 线程池隔离:不同业务模块使用独立线程池配置类,避免单模块故障影响全局;
- 压测验证:上线前通过压测调整核心线程数 / 队列容量,适配业务并发量。
十一、源码
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线程池及监控任务关闭完成");
}
}