现象问题
最近看到线上的项目线程数过大的报警,开始还是不知道什么原因,因为很多项目都使用同样的线程池管理代码,认为那个项目是偶然的因素造成的,后来经过分析,发现线程数每天都在增加。其他的项目由于发布导致线程会从零开始计算,所以没有那么快达到报警值。 触发报警的代码大概如下:
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 { } catch (Exception e) {
} finally { } }); } catch (Exception e) { } } }); thread.start(); }
public int getMaxThreadCount(){ return ....; } public void stop(){ this.start=false; }
|
上面的代码存在两个问题:
start
是个主线程的变量,在主线程修改值,子线程的while
循环不会停止
上述代码能够停止,因为在内部调用`Thread.sleep方法,导致线程内的变量刷新
newFixedThreadPool
线程池没有调用shutdown方法,导致线程不会被回收。
改正方法:
start
设置成线程共享变量volatile
类型
在最后调用停止的时候,让线程池进行回收
修改后代码如下:
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 { } 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为什么没有把线程池对象回收?是怎么做到的?
目前还没有找到问题的答案,等找到后回来更新。