1. 程式人生 > 其它 >執行緒池踩坑 - 作為例項成員或方法區域性變數的誤區

執行緒池踩坑 - 作為例項成員或方法區域性變數的誤區

執行緒池可以把執行緒複用起來,減少執行緒建立銷燬的時間和資源消耗,提高了程式任務執行的吞吐率。就像執行緒屬於全域性使用的資源一樣,執行緒池一般也是全域性性,對整個應用程序的執行緒複用做有效的管理。設計者一般都會把執行緒池作為類的靜態成員或者單例成員,存活於整個程序的生命週期。
但是還是例外地看到了類似這樣的程式碼,比如放到了方法體中作為區域性變數:

private static void sampleFunc() {
    ExecutorService executor = Executors.newFixedThreadPool(10);
    for (int i = 0; i < 100; i++) {
        executor.execute(
new Runnable() { @Override public void run() { ... } }); } }

這些執行緒池的使用看起來挺正常的,隱藏著一個很嚴重的問題:
當物件例項不再使用或者方法執行完畢後,什麼時候會釋放執行緒 ,關閉執行緒池?
不同的執行緒池表現不一樣。主要看是否設定了核心執行緒數。

  • 如果沒有設定核心執行緒數,比如 newCachedThreadPool ,線上程池的執行緒空閒時間到達 60s 後,執行緒會關閉,所有執行緒關閉後執行緒池也相應關閉回收。
  • 如果設定了核心執行緒數,比如 newSingleThreadExecutor 和 newFixedThreadPool ,如果沒有主動去關閉,或者設定核心執行緒的超時時間,核心執行緒會一直存在不會被關閉,這個執行緒池就不會被釋放回收。

驗證如下:

    public static void main(String[] args) {
        while (true) {
            try {
                ExecutorService service = Executors.newFixedThreadPool(1);
                service.submit(
new Runnable() { @Override public void run() { try { Thread.sleep(2000); ////模擬處理業務 } catch (InterruptedException e) { } } }); service = null; } catch (Exception e) { } try { Thread.sleep(2000); System.gc(); } catch (InterruptedException e) { } } }

執行後,檢視jvm,會發現執行緒每2秒就增長一個。如下圖所示: