线程异常增长

小组群内leader甩出来一个截图,说是线程有些异常,一直在增长,从pool-1-thread一直到pool-285-thread,每个pool都只有三个线程:thread-1 thread-2 thread-3,第一反应感觉肯定和定时任务有关,这种稳定的有规律的一般都是和定时任务有关。

  1. 定位线程是哪部分的代码

    1. 如果线程池自定义名字了,直接在项目里搜就可以

    2. 直接查询最新的线程的日志,比如这里就是查询pool-285-thread对应的日志,如果有日志的话,很大概率就能直接定位到是哪里的问题,很不幸,我们出问题的这部分当时没有打印日志

    3. 通过jstack分析

  2. 分析代码

task(){
ExecutorService executorService = Executors.newFixedThreadPool(6);
executorService.execute(()->{统计1})
executorService.execute(()->{统计2})
executorService.execute(()->{统计3})
}

线上出问题的代码大概是上面的逻辑,根据现象我们知道,一共开启了285个线程池,线程池每次有三个线程在工作。

为什么会开启这么多线程池?

由于Executors.newFixedThreadPool(6)是放在类里面,且没有手动shutdown,导致每次定时任务执行这个方法的时候都会开启一个线程池

为什么线程池里的线程没有回收?

newFixedThreadPool最大线程和核心线程数量是一样的,即这里创建了6个核心线程,而核心线程的回收又得手动指定allowCoreThreadTimeOut为true才可以,所以并不会被回收

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
  1. 复现验证
public class Case {
    void fixPoolInc() {
        ExecutorService executorService = Executors.newFixedThreadPool(6);
        for (int i = 0; i < 3; i++) {
            executorService.execute(() -> {
                System.out.println("ThreadName:" + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    @SneakyThrows
    public static void main(String[] args) {
        Case aCase = new Case();
        for (int i = 0; i < 10; i++) {
            Thread.sleep(5000);
            aCase.fixPoolInc();
        }
    }
}

开10个线程池,每个线程池启动3个线程,通过jconsole观察是否线程持续增长,并没有回收

  1. 结论

    1. 方法内部开启的线程池记得手动关闭,或者将方法内部的线程池提到外部

    2. 最好还是通过ThreadPoolExecutor手动设置每个参数,这样对于业务更清晰,而不是偷懒使用ExecutorService

    3. 线程池最好还是指定明细,这边方便排查异常

参考文档:

Java线程Dump分析_线程dump怎么分析_萨达哈鲁君的博客-CSDN博客

idea + jconsole实现线程监控_idea查看线程运行情况_黑夜伴白行的博客-CSDN博客

深入浅出线:程池的线程回收–回收的是非核心线程吗?_非核心线程的是怎么回收的_小猪快跑22的博客-CSDN博客

Licensed under CC BY-NC-SA 4.0
最后更新于 2024-10-18