スレッドの例外補足

なんか、スレッドの異常系のテストをしたいなーと思ったら、スレッドってほとんどやったことなかったので、けっこうてこずりました。


以下のTest001を実行しても、exeメソッドの中でRuntimeExceptionがキャッチできない。

public class Test001 {
  
  public static void main(String[] args) throws Exception {
    new Test001().exe();
  }
  
  private void exe() throws Exception {
    
    System.out.println("START");

    MyThread thread = new MyThread();
    
    try {
      thread.start();
      thread.join();
    } catch (Throwable e) {
      System.out.println("exeメソッド内でキャッチ : " + e);
      throw e;
    }
    System.out.println("END");
  }
  
  // スレッド
  class MyThread extends Thread {
    public void run() {
      if(true) throw new RuntimeException(this.getName());
    }
  }
}

実行結果

START
END


スレッド内の例外をスレッド呼び出し元でキャッチする場合は、JDK1.4以下はThreadGroupのサブクラスを作る必要があるみたい。

public class Test001 {
  
  public static void main(String[] args) throws Exception {
    new Test001().exe();
  }
  
  private void exe() throws Exception {
    
    System.out.println("START");
    
    MyThreadGroup threadGroup = new MyThreadGroup("MyThreadGroup");
    MyThread thread = new MyThread(threadGroup, "MyThreadGroup");
    
    thread.start();
    thread.join();
    
    if (threadGrpup.getThrowable() != null) {
      System.out.println("exeメソッド内でキャッチ : " + threadGrpup.getThrowable());
    }
    System.out.println("END");
  }
  
  // スレッド
  class MyThread extends Thread {

    public MyThread(ThreadGroup threadGroup, String name) {
      super(threadGroup, name);
    }
    public void run() {
      if(true) throw new RuntimeException(this.getName());
    }
  }
  
  // スレッドグループ
  class MyThreadGroup extends ThreadGroup {

    private Throwable throwable = null;
    
    public MyThreadGroup(String name) {
      super(name);
    }
    public void uncaughtException(Thread t, Throwable e) {
      throwable = e;
    }
    public Throwable getThrowable() {
      return throwable;
    }
  }
}

これで実行結果は以下のようになりました。

START
exeメソッド内でキャッチ : java.lang.RuntimeException: MyThreadGroup
END

Thread.join()で子スレッド終了を待ち、スレッドグループがなくなるので、以下のようになります。
thread.join();の前ならthread.getThreadGroup()でThreadGroupインスタンスが取得できるけど、
thread.join();の後だとthread.getThreadGroup()を呼び出してもnullです。


JDK1.5以上はこんな感じかなー。

public class Test001 {
  
  public static void main(String[] args) throws Exception {
    new Test001().exe();
  }
  
  private void exe() throws Exception {
    
    System.out.println("START");
    
    MyThread thread = new MyThread();
    MyUncaughtExceptionHandler handler = new MyUncaughtExceptionHandler();
    
    thread.setUncaughtExceptionHandler(handler);
    
    thread.start();
    thread.join();
    
    System.out.println("exeメソッド内でキャッチ : " + handler.getThrowable());
    System.out.println("END");
  }
  
  class MyThread extends Thread {
    public void run() {
      if(true) throw new RuntimeException(this.getName());
    }
  }
  
  class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {
  
    private Throwable throwable = null;

    public void uncaughtException(Thread t, Throwable e) {
      throwable = e;
    }
    public Throwable getThrowable() {
      return throwable;
    }
  }
}

これで実行結果は以下のようになりました。

START
exeメソッド内でキャッチ : java.lang.RuntimeException: Thread-0
END

Thread.join()で子スレッド終了を待ち、スレッドグループがなくなるので、以下のようになります。
thread.join();の前ならthread.getUncaughtExceptionHandler()でMyUncaughtExceptionHandlerインスタンスが取得できるけど、
thread.join();の後だとthread.getUncaughtExceptionHandler()を呼び出してもnullです。