jstack 1 | grep -o 'pool-[0-9]*-thread' | sort | uniq -c | sort -rn | head
jcmd 1 Thread.print | grep -c 'java.lang.Thread.State'

服务跑了三天,Threads 从 120 涨到 812,jstack 里大量 pool-137-thread-1 这种编号,每个池只有一两个线程,都没有退出。先别急着动参数,把报错原文和时间点记下来,顺序反了容易白折腾。

原因不复杂,工具类里每次调用都 Executors.newFixedThreadPool,用完没有 shutdown。池里的核心线程默认不回收,创建者对象也被线程持有,数量只增不减。

「线程池泄漏」排查到最后往往是很小的一处差异,前面那些复杂猜测可以先放一放。改动写完就同步给同事,下一个人踩同一个坑,成本还是你的。

验证用真实数据走一遍,测试数据太干净,边界情况根本碰不到。能分两步做的操作就别合成一步,中间看一眼返回值,出问题好判断。

处理上按这个顺序:全局只保留少量固定线程池 通过注入复用;确实要临时池的 用完在 finally 里 shutdown;线程工厂的命名带上业务前缀 方便在 jstack 里认;连续观察两天线程数曲线 确认不再单调上升。

改完只确认一件事:线程数稳定在 150 上下,连续两天不随请求量上涨。

作者 9823189