newFixedThreadPool執行緒池導致執行緒泄漏
- 2020 年 1 月 21 日
- 筆記
現象問題
最近看到線上的項目執行緒數過大的報警,開始還是不知道什麼原因,因為很多項目都使用同樣的執行緒池管理程式碼,認為那個項目是偶然的因素造成的,後來經過分析,發現執行緒數每天都在增加。其他的項目由於發布導致執行緒會從零開始計算,所以沒有那麼快達到報警值。 觸發報警的程式碼大概如下:
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; }
上面的程式碼存在兩個問題:
-
start
是個主執行緒的變數,在主執行緒修改值,子執行緒的while
循環不會停止 上述程式碼能夠停止,因為在內部調用`Thread.sleep方法,導致執行緒內的變數刷新 -
newFixedThreadPool
執行緒池沒有調用shutdown方法,導致執行緒不會被回收。
改正方法:
-
start
設置成執行緒共享變數volatile
類型 - 在最後調用停止的時候,讓執行緒池進行回收
修改後程式碼如下:
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為什麼沒有把執行緒池對象回收?是怎麼做到的?
目前還沒有找到問題的答案,等找到後回來更新。