Java threadにおける例外の処理ポリシー

8049 ワード

前言
    この話題を議論しようとしてしばらく経った.数年前に面接に行ったのを覚えています.似たような質問をしたことがあります.java threadにおける異常に対する処理状況である.Java thread自体が同時、ロックなどの問題に関連しているため、複雑です.さらに異常処理を加えて、これらのものをより特殊にします.要約すると,三つの主要な問題にほかならない.1.javaが起動したスレッドに異常を投げ出すことはできますか?2.起動したスレッドで異常をキャプチャできますか?3.異常をキャプチャできる場合、checked exceptionとunchecked exceptionについて、それぞれどのような処理がありますか?
     今、私たちは一つ一つ討論します.
スレッドから例外が放出されます
    スレッドに異常を投げてみましょう.我々の理解によれば,ある方法に異常を投げ込むと仮定し,その定義の方法ヘッダにも声明を加える必要がある.最も簡単な方法は次の通りです.
public class Task implements Runnable {

	@Override
	public void run() throws Exception {
		int number0 = Integer.parseInt("1");
		throw new Exception("Just for test");
	}
}

    しかし、上のコードをコンパイルすると、コンパイルできないことに気づきます.システム・レポートのエラーは次のとおりです.
Task.java:3: error: run() in Task cannot implement run() in Runnable
    public void run() throws Exception {
                ^
  overridden method does not throw Exception
1 error

     このような方法は通用しないことに気づいた.つまり,スレッドに異常を直接投げてはいけない.しかし、スレッドコードに異常が発生した場合、どうすればいいのでしょうか.たとえば、スレッドを介していくつかのファイルにアクセスしたり、ネットワークをIO操作したりすると、異常が発生します.あるいは、いくつかのリソースにアクセスしたときにシステムがクラッシュしました.このようなシーンは確かに発生する可能性があります.私たちはこれらの状況についてさらに議論する必要があります.
異常処理のいくつかの方式
     前述したいくつかのスレッドアクセスリソースに異常が発生した.例えば、ファイルシステムにアクセスすると、IOException、FileNotFoundExceptionなどの異常が投げ出されることがわかります.私たちがアクセスしたコードでは、実際には2つの方法で処理する必要があります.1つは、リソースを変更するメソッドヘッダを使用して、throws IOException、FileNotFoundExceptionなどの異常な修飾を追加することです.もう1つは、この部分のコードブロックに直接try/catch部分を追加することです.前述の議論から,メソッド宣言にthrows Exceptionを加える方法は通用しないことが分かった.ではtry/catchという方法しかありません.
    また,異常の処理において,一般的な異常はchecked exceptionとunchecked exceptionに分けられることも知られている.unchecked exceptionとして、彼らは通常、Error、OutOfMemoryError、またはシステムが直接崩壊したなどの深刻なシステムエラーまたはシステム設計エラーを指す.このような異常が発生した場合、私たちは普通どうしようもなく回復できません.では、このような状況が発生したら、私たちはどのように処理しますか?
checked exception
     スレッドの中でchecked exceptionを処理し、私たちの以前の理解によると、私たちはそれを直接キャプチャして処理することができます.いくつかのthreadの例でも見たことがあります.たとえば、次のコードの一部です.
import java.util.Date;
import java.util.concurrent.TimeUnit;

public class FileLock implements Runnable {
	@Override
	public void run() {
		for(int i = 0; i < 10; i++) {
			System.out.printf("%s
", new Date()); try { TimeUnit.SECONDS.sleep(1); } catch(InterruptedException e) { System.out.printf("The FileClock has been interrupted"); } } } }

     スレッド実行コードを定義し,ここではTimeUnit.SECONDS.sleep()メソッドを呼び出すために例外をキャプチャする必要がある.この方法自体がInterruptedExceptionを投げ出すので、try/catchブロックで処理しなければなりません.
     スレッドを起動し、対話するコードは次のとおりです.
import java.util.concurrent.TimeUnit;

public class Main {
	public static void main(String[] args) {
		// Creates a FileClock runnable object and a Thread
		// to run it
		FileClock clock=new FileClock();
		Thread thread=new Thread(clock);
		
		// Starts the Thread
		thread.start();
		try {
			// Waits five seconds
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			e.printStackTrace();
		};
		// Interrupts the Thread
		thread.interrupt();
	}
}

     この部分のコードはFileLockスレッドを起動し、中断しようとします.実行時にFileLockで実行されたコードが異常を正常に処理できることがわかります.
    したがって、threadではchecked exceptionを処理するには、簡単なtry/catchブロックでいいです.
unchecked exception
     このunchecked exceptionでは、相対的に違います.実際、Threadの定義には、setuUncautheExceptionHandler(UncautheExceptionHandler)という方法があります.この方法は、unchecked exceptionを処理するために使用できます.では、このような状況のシーンはどうなっているのでしょうか.
    setuUncaughtExceptionHandler()メソッドは、イベント登録のエントリに相当します.jdkでは、この方法の定義は以下の通りです.
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
    checkAccess();
    uncaughtExceptionHandler = eh;
}

     UncaughtExceptionHandlerは次のように宣言されたインタフェースです.
public interface UncaughtExceptionHandler {
    /**
     * Method invoked when the given thread terminates due to the
     * given uncaught exception.
     * <p>Any exception thrown by this method will be ignored by the
     * Java Virtual Machine.
     * @param t the thread
     * @param e the exception
    */
    void uncaughtException(Thread t, Throwable e);
}

     異常が発生した場合、入力したUncaughtExceptionメソッドが呼び出されます.
     前述の議論を総合すると,handle unchecked exceptionを実現する方法の具体的な手順は以下のようにまとめることができる.
1.クラス実装UncaughtExceptionHandlerインタフェースを定義します.実装の方法には異常処理に対する論理と手順が含まれている.
2.スレッド実行構造と論理を定義します.このステップは、通常のスレッド定義と同じです.
3.サブスレッドを作成して実行する方法でthread.start()文の前にthread.setuUncautExceptionHandler文を追加し、処理ロジックの登録を実現します.
    次に、ここで定義した手順に従って例を示します.
    まず、UncaughtExceptionHandlerインタフェースを実装するセクションです.
import java.lang.Thread.UncaughtExceptionHandler;

public class ExceptionHandler implements UncaughtExceptionHandler {
	public void uncaughtException(Thread t, Throwable e) {
		System.out.printf("An exception has been captured
"); System.out.printf("Thread: %s
", t.getId()); System.out.printf("Exception: %s: %s
", e.getClass().getName(), e.getMessage()); System.out.printf("Stack Trace:
"); e.printStackTrace(System.out); System.out.printf("Thread status: %s
", t.getState()); } }

     ここで追加した異常処理ロジックは簡単ですが、スレッドの情報と異常情報を印刷するだけです.
    次に、スレッドの内容を定義します.ここでは、スレッドにunchecked exceptionを生成させます.
public class Task implements Runnable {

	@Override
	public void run() {
		int number0 = Integer.parseInt("TTT");
	}
}

     このコードから、Integer.parseInt()のパラメータが間違っていて、異常が投げ出されるに違いありません.
    次に、作成スレッドと登録処理ロジックの部分を追加します.
public class Main {
	public static void main(String[] args) {
		Task task = new Task();
		Thread thread = new Thread(task);
		thread.setUncaughtExceptionHandler(new ExceptionHandler());
		thread.start();
	}
}

     プログラム全体を実行すると、次のような結果が得られます.
An exception has been captured
Thread: 8
Exception: java.lang.NumberFormatException: For input string: "TTT"
Stack Trace: 
java.lang.NumberFormatException: For input string: "TTT"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:492)
	at java.lang.Integer.parseInt(Integer.java:527)
	at Task.run(Task.java:5)
	at java.lang.Thread.run(Thread.java:722)
Thread status: RUNNABLE

    この部分の出力は,我々が前に実装したUncaughtExceptionHandlerインタフェースの定義である.
    したがって,unchecked exceptionについても,類似イベント登録のメカニズムを用いてある程度処理することができる.
まとめ
     Java threadの異常に関する部分は奇抜です.1つのスレッドで異常を投げ出すことはできません.一般的にスレッドでchecked exceptionに遭遇するが,try/catchブロックを用いて処理することが推奨される.一方、unchecked exceptionでは、UncaughtExceptionHandlerインタフェースを実装するオブジェクトインスタンスを登録して処理するのが合理的です.これらの細部のものは出会ったことがなければ確かに答えにくい.
きじゅんざいりょう
Java 7 Concurrency Cookbook
http://stackoverflow.com/questions/1369204/how-to-throw-a-checked-exception-from-a-java-thread/1369828#1369828
http://stackoverflow.com/questions/6546193/how-to-catch-an-exception-from-a-thread