newFixedThreadPool线程池导致线程泄漏

现象问题

最近看到线上的项目线程数过大的报警,开始还是不知道什么原因,因为很多项目都使用同样的线程池管理代码,认为那个项目是偶然的因素造成的,后来经过分析,发现线程数每天都在增加。其他的项目由于发布导致线程会从零开始计算,所以没有那么快达到报警值。 触发报警的代码大概如下:

boolean start=true;
public void doSomeThing(){
    ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
    Thread thread = new Thread(() -> {
        while (start) {
            try {
                if(.//判断能否执行){
                    Thread.sleep(100);
                    return;
                }
                executorService.execute(() -> {
                    try {
                        //....your code
                    } catch (Exception e) {

                    } finally {

                    }
                });
            } catch (Exception e) {
            }
        }
    });
    thread.start();
}

public int getMaxThreadCount(){
    return ....;
}
public void stop(){
    this.start=false;
}

上面的代码存在两个问题:

  1. start是个主线程的变量,在主线程修改值,子线程的while循环不会停止

    上述代码能够停止,因为在内部调用`Thread.sleep方法,导致线程内的变量刷新

  2. newFixedThreadPool 线程池没有调用shutdown方法,导致线程不会被回收。

改正方法:

  1. start 设置成线程共享变量volatile类型

  2. 在最后调用停止的时候,让线程池进行回收

修改后代码如下:


volatile boolean start=true;
ExecutorService executorService;
Thread thread ;
public void doSomeThing(){
    executorService = Executors.newFixedThreadPool(nThreads);
    thread = new Thread(() -> {
        while (start) {
            try {
                 if(.//判断能否执行){
                    Thread.sleep(100);
                    return;
                }
                executorService.execute(() -> {
                    try {
                        //....your code
                    } catch (Exception e) {

                    } finally {

                    }
                });
            } catch (Exception e) {
            }
        }
    });
    thread.start();
}

public int getMaxThreadCount(){
    return ....;
}
public void stop(){
    this.start=false;
    if (executorService != null) {
        executorService.shutdown();
        executorService.awaitTermination(MaxConcurrency() * 3, TimeUnit.SECONDS);
        for (int i = 0; i < 10 && !executorService.isTerminated(); i++) {
            Thread.sleep(1000);
        }
    }
    if (thread != null && thread.isAlive()) {
        thread.interrupt();
        thread.join(2000);
    }
}

最后的疑问

线程池在最后不使用后,为什么线程没有被释放?GC为什么没有把线程池对象回收?是怎么做到的?

目前还没有找到问题的答案,等找到后回来更新。

newFixedThreadPool线程池导致线程泄漏

http://blog.laofu.online/2019/02/21/2019-02-21-thread-leak/

作者

付威

发布于

2019-02-21

更新于

2020-08-10

许可协议

评论