ExecutorService 的使用

 

ExecutorService 是大神Doug Lea在併發包裏爲我們提供的一個便攜式的線程池的操作接口,

一般使用Executors 來創建各種類型的線程池,大家比較臉熟的是下面四種
 
newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。
注意:上面這個創建的核心線程數是0最大線程數默認是Integer.MaxValue,高併發下創建這麼多線程會對JVM造成很大的負擔 很可能OOM
newFixedThreadPool 創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
newScheduledThreadPool 創建一個定長線程池,支持定時及週期性任務執行。
注意:上面這兩個創建的隊列長度默認是Integer.MaxValue,高併發下回堆積線程,造成OOM
 
這裏生產環境還是不太推薦大家使用這種方式的線程池,因爲它創建的線程池的都是默認配置會造成我們寶貴的資源的浪費,比如使用的DefaultThreadFactory工廠,它創建的線程都是非守護線程, ,有時候免費的東西往往套路深啊
 
部分代碼

 
public static void main(String[] args) {
        //基本信息
  FutureTask uinfo = new FutureTask(()->{
           System.out.println("這裏模擬uinfo調用其他API接口邏輯");
           return "user信息的結構";
  });

    //線程池
   ExecutorService executorService = Executors.newFixedThreadPool(2);
   executorService.submit(uinfo);
  System.out.println("執行結果:"+uinfo.get());
}
 
問題來了。上面的代碼有沒有問題?why?
......邏輯等待10S.....
答案是 肯定有問題。如果上面的代碼是你的生產環境。那麼你們的運維同學可能要拿刀砍你了,這個代碼上線後隨着時間推移隨時都有可能把服務器搞崩,先從簡單的現象來分析,一個java的man方法執行完畢後會打印一個日誌Process finished with exit code 0,0代表正常結束 ,上面的代碼你會發現,這句日誌遲遲不回出現。爲什麼呢,結果也執行完了按理說應該一切都結束了纔對呀,原因是 ExecutorService 的內部實現機製造成的。及時是main方法已經執行完畢了,但是ExecutorService 內部開啓了等待加入新任務的任務。所以呢main方法雖然執行結束了但是ExecutorService 還在JVM裏偷偷的跑着,在生產環境併發環境下就會創建出一個又一個的ExecutorService 對象並且還不能被回收,最後JVM只能被撐爆拋出OOM了
 
.....運維小哥哥開始換成鈍刀了......
 
有人說調用.shutdown()方法就好了,沒錯,但是 不完美
 
擴展下,其實這裏還是不夠的,shudown()方法有通知的作用,就是告訴ExecutorService你可以關閉了,那什麼時候關閉呢,立馬關閉還是一年後關閉呢,awaitTermination(awaitTime, TimeUnit.MILLISECONDS)方法可以來設置已給結束時間,
等到了時間,使用shutdownNow()方法來徹底停掉
 
所以正確的調用順序是:
shutdown方法
awaitTermination方法
shutdownNow方法(發生異常或者是Timeout的時候)
 
shutdown方法調用後,就不能再繼續使用ExecutorService來追加新的任務了,如果繼續調用execute方法執行新的任務的話,就會拋出RejectedExecutionException異常。(submit方法也會拋出上述異常)
 
awaitTermination方法也不是在它被調用的時間點上簡單得等待任務結束而是在awaitTermination方法調用後,持續監視各個任務的狀態以或者是否線程已經運行結束。所以不調用shutdown方法執行調用awaitTermination的話由於追加出來的任務可能會導致任務狀態監視出現偏差而發生預料之外的awaitTermination的Timeout異常
 
最後貼出完整代碼
public static void main(String[] args) throws InterruptedException {
   //基本信息
   FutureTask uinfo = new FutureTask(()->{
           System.out.println("這裏模擬uinfo調用其他API接口邏輯");
           return "user信息的結構";
  });

     //線程池
   ExecutorService executorService = Executors.newFixedThreadPool(2);
   executorService.submit(uinfo);

  System.out.println("執行結果:"+uinfo.get());
  
   //呼叫總檯可以關機
  executorService.shutdown();
   //總檯:10秒後關機
   if(!executorService.awaitTermination(10, TimeUnit.SECONDS)){
          //總檯:執行關機
     executorService.shutdownNow();
  }
 //加上這些代碼運維同學默默的放下了手裏的刀...珍愛生命,有始有終...
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章