1. 程式人生 > >java多執行緒的未捕獲異常處理機制

java多執行緒的未捕獲異常處理機制

 一:為什麼要單獨講多執行緒的異常捕捉呢?

先看個例子:

public class ThreadException implements Runnable{
  @Override
  public void run() {
    throw new RuntimeException();
  }
  //現象:控制檯打印出異常資訊,並執行一段時間後才停止
  public static void main(String[] args){
    //就算把執行緒的執行語句放到try-catch塊中也無濟於事
    try{
      ExecutorService exec = Executors.newCachedThreadPool();
      exec.execute(new ThreadException());
    }catch(RuntimeException e){
      System.out.println("Exception has been handled!");
    }
  }
}

在run中手動丟擲了一個執行時異常,在main中啟動執行緒,catch語句塊中捕捉下異常,捕捉到列印一句話。執行結果如下圖:

 

發現異常被拋到了控制檯,沒有列印catch塊中的語句。

  結論:多執行緒執行不能按照順序執行過程中捕獲異常的方式來處理異常,異常會被直接丟擲到控制檯(由於執行緒的本質,使得你不能捕獲從執行緒中逃逸的異常。一旦異常逃逸出任務的run方法,它就會向外傳播到控制檯,除非你採用特殊的形式捕獲這種異常。),這樣會讓你很頭疼,無法捕捉到異常就無法處理異常而引發的問題。

  於是,我們一定會想如何在多執行緒中捕捉異常呢?

二、多執行緒中捕捉異常

  我們來按照下面的步驟完成這次實驗:

  1.定義異常處理器

   要求,實現 Thread.UncaughtExceptionHandler的uncaughtException方法,如下:

/*
 * 第一步:定義符合執行緒異常處理器規範的“異常處理器”
 * 實現Thread.UncaughtExceptionHandler規範
 */
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
  /*
   * Thread.UncaughtExceptionHandler.uncaughtException()會線上程因未捕獲的異常而臨近死亡時被呼叫
   */
  @Override
  public void uncaughtException(Thread t, Throwable e) {
    System.out.println("caught  "+e);
  }
}

 2.定義使用該異常處理器的執行緒工廠

/*
 * 第二步:定義執行緒工廠
 * 執行緒工廠用來將任務附著給執行緒,並給該執行緒繫結一個異常處理器
 */
class HanlderThreadFactory implements ThreadFactory{
  @Override
  public Thread newThread(Runnable r) {
    System.out.println(this+"creating new Thread");
    Thread t = new Thread(r);
    System.out.println("created "+t);
    t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());//設定執行緒工廠的異常處理器
    System.out.println("eh="+t.getUncaughtExceptionHandler());
    return t;
  }
}

3.定義一個任務,讓其丟擲一個異常

/*
 * 第三步:我們的任務可能會丟擲異常
 * 顯示的丟擲一個exception
 */
class ExceptionThread implements Runnable{
  @Override
  public void run() {
    Thread t = Thread.currentThread();
    System.out.println("run() by "+t);
    System.out.println("eh = "+t.getUncaughtExceptionHandler());
    throw new RuntimeException();
  }
}

 4.呼叫實驗

/*
 * 第四步:使用執行緒工廠建立執行緒池,並呼叫其execute方法
 */
public class ThreadExceptionUncaughtExceptionHandler{
  public static void main(String[] args){
    ExecutorService exec = Executors.newCachedThreadPool(new HanlderThreadFactory());
    exec.execute(new ExceptionThread());
  }
}

 執行結果如下圖:

三、結論

  在java中要捕捉多執行緒產生的異常,需要自定義異常處理器,並設定到對應的執行緒工廠中(即第一步和第二步)。

四、拓展

  如果你知道將要在程式碼中處處使用相同的異常處理器,那麼更簡單的方式是在Thread類中設定一個靜態域,並將這個處理器設定為預設的未捕獲處理器。

這個處理器只有在不存線上程專有的未捕獲異常處理器的情況下才會被呼叫。

public static void main(String[] args){
    Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
    ExecutorService exec =Executors.newCachedThreadPool();
    exec.execute(new ExceptionThread());
}