线程池又爆了
背景
年前某地TMAC监控发出告警信息,某应用系统线程数过多,超过5000了,于是联系相应系统研发人员进行预警,得到回复说这不是问题。但根据我的经验,线程数这么大,怎么都会是个问题,干了好多年,第一次见每个系统的系统数是如此之多。
排查
既然没有达成一致,那就自己动手。先找到现场的运维同学,要了线程信息,通过jstack导入进行分析。其实也不用分析啥,问题一目了然,都不需要进到具体线程内部去看它在干嘛。
?大量线程的名字很有规律,都是pool-\d+-thread-1?线程状态都是waiting on condition
这两个信息只能说明一个问题,就是在方法内部创建了线程池,但是没有shutdown。
"pool-2494-thread-1" prio=10 tid=0x00007f885014c800 nid=0xc06d waiting on condition [0x00007f86d20fe000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000608e437c8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
那如何查到底是哪出现的线程池泄露呢。
?方法一?如果不启动项目调试的话,可以搜关键字Executors或ThreadPool,不过如果是偷偷在引用的jar包里干的就不好查了?通过线程名字来查,但是很可惜,本案例中,研发人员没有通过ThreadFactory去定义线程池的名字,所以没法找。?方法二?如果通过关键字搜索不好找的话(比如在某个引用的jar包里导致的),可以启动项目, 在Thread.init上加断点,最好是加上条件断点,当name.startsWith("pool-")时才生效,这样可以提高排查效率。private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
结论
?尽量不要在方法内部创建线程池,可以按业务统筹规划线程池,或者直接new Thread也行。?使用线程池一定要通过ThreadFactory指定线程名称,便于后续问题分析和排查。