newFixedThreadPool线程池导致线程泄漏

现象问题

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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. 在最后调用停止的时候,让线程池进行回收

修改后代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

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为什么没有把线程池对象回收?是怎么做到的?

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