Java線程池中的異常處理

Java線程池中的異常處理

原文博客

§ 前置知識

  • 線程池中的任務有兩種,一種有返回值,一種無返回值。通常對應着兩種提交任務的方法:

    • submit方法:雖然參數是Runnable,但由於返回值爲Future,所以通常傳入的參數爲FutureTask類的對象。(FutureTask間接實現了Runnable接口和Future接口)

      public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
      }
      
    • execute方法:

      public void execute(Runnable command) {
        if (command == null)
          throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
          if (addWorker(command, true))
            return;
          c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
          int recheck = ctl.get();
          if (! isRunning(recheck) && remove(command))
            reject(command);
          else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
        }
        else if (!addWorker(command, false))
          reject(command);
      }
      
  • 不論是什麼類型的任務,最終被包裝如ThreadPoolExecutor類中的Worker類,而任務的執行都是通過runWorker()方法,這裏截取關鍵部分:

    try {
      beforeExecute(wt, task);
      Throwable thrown = null;
      try {
        task.run(); // 調用對應任務的run方法!!!
      } catch (RuntimeException x) {
        thrown = x; throw x;
      } catch (Error x) {
        thrown = x; throw x;
      } catch (Throwable x) {
        thrown = x; throw new Error(x);
      } finally {
        afterExecute(task, thrown);
      }
    } finally {
      task = null;
      w.completedTasks++;
      w.unlock();
    }
    

§ 默認情況存在的問題

由前置知識可知,runWorker()方法內,若task.run()出現異常,會拋出,然後進入afterExecute(task, thrown)方法,而該方法默認爲空,所以默認情況下我們無法得到想要的異常信息。針對submit和execute提交的有返回值和無返回值的任務,有兩種解決方向如下。

§ submit提交的有返回值的任務

由於任務是FutureTask類的,所以在執行task.run()時,執行的是FutureTask中重寫的run()方法,截取關鍵部分如下:

try {
  result = c.call();
  ran = true;
} catch (Throwable ex) {
  result = null;
  ran = false;
  setException(ex);
}
//////
protected void setException(Throwable t) {
  if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
    outcome = t; // !!!!關鍵
    UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
    finishCompletion();
  }
}

***可見,任務執行出現異常後,不拋出,而是存入返回值當中。因此,若要獲取異常信息則需通過返回值獲取,所以可以在try catch中通過FutureTask的get()方法獲取返回結果(異常):***

try{
	future.get();
}catch(xxxException){
	// do sth
}

§ 通過execute提交的無返回值的任務

【法1】

從前面所講可以知道,無返回值的任務,即原生Runnable,在執行task.run()時若有異常則會拋出,並只能在afterExecute(task, thrown)中進行自定義的處理,***所以可以自定義線程池,繼承ThreadPoolExecutor並複寫其afterExecute(Runnable r, Throwable t)方法。***


【法2】

***實現Thread.UncaughtExceptionHandler接口,並重寫實現void uncaughtException(Thread t, Throwable e);方法,在該方法中處理異常。並將該handler傳遞給線程池的ThreadFactory。***

具體可參考:https://juejin.im/post/5d27c3e6518825451f65ee15

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章