web-dev-qa-db-ja.com

FutureTaskで例外をキャッチする方法

Java 1.6(およびEclipseから)のExecutors.newCachedThreadPool()で実行されているFutureTaskが、Runnable.run()メソッドの例外を飲み込むことを発見した後、私は私のすべてのRunnable実装にthrow/catchを追加せずに、これらをキャッチする方法を考え出そうとしました。

APIは、FutureTask.setException()をオーバーライドすることがこれに役立つはずであることを示唆しています。

このFutureがすでに設定されているかキャンセルされていない限り、このFutureは、指定されたthrowableを原因としてExecutionExceptionを報告します。このメソッドは、計算の失敗時にrunメソッドによって内部的に呼び出されます。

ただし、このメソッドは呼び出されていないようです(デバッガーで実行すると、例外がFutureTaskによってキャッチされますが、setExceptionは呼び出されません)。問題を再現するために、次のプログラムを作成しました。

public class RunTest {
    public static void main(String[] args) {
        MyFutureTask t = new MyFutureTask(new Runnable() {

            @Override
            public void run() {
                throw new RuntimeException("Unchecked exception");

            }
        });

        ExecutorService service = Executors.newCachedThreadPool();
        service.submit(t);
    }
}

public class MyFutureTask extends FutureTask<Object> {

    public MyFutureTask(Runnable r) {
        super(r, null);
    }

    @Override
    protected void setException(Throwable t) {
        super.setException(t);
        System.out.println("Exception: " + t);
    }
}

私の主な質問は、FutureTaskでスローされた例外をキャッチするにはどうすればよいですか? setExceptionが呼び出されないのはなぜですか?

また、なぜThread.UncaughtExceptionHandlerメカニズムはFutureTaskによって使用されていませんが、これには何か理由がありますか?

21
Thirler

setExceptionはおそらくオーバーライド用には作成されていませんが、必要に応じて結果を例外に設定できるようにするために提供されています。やりたいことは、done()メソッドをオーバーライドして、結果を取得しようとすることです。

public class MyFutureTask extends FutureTask<Object> {

    public MyFutureTask(Runnable r) {
        super(r, null);
    }

    @Override
    protected void done() {
        try {
            if (!isCancelled()) get();
        } catch (ExecutionException e) {
            // Exception occurred, deal with it
            System.out.println("Exception: " + e.getCause());
        } catch (InterruptedException e) {
            // Shouldn't happen, we're invoked when computation is finished
            throw new AssertionError(e);
        }
    }
}
20
gustafc

UncaughtExceptionHandler を使用してみましたか?

  • UncaughtExceptionHandler インターフェースを実装する必要があります。
  • プールスレッドにUncaughtExceptionHandlerを設定するには、Executor.newCachedThreadPool(ThreadFactory)呼び出しで ThreadFactory を指定します。
  • 作成されたスレッドのUncaughtExceptionHandlerは、setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)を介して設定できます。

ExecutorService.execute を使用してタスクを送信します。これは、executeを使用して送信されたタスクからスローされた例外のみが、捕捉されなかった例外ハンドラーに到達するためです。 ExecutorService.submit で送信されたタスクの場合、スローされた例外はすべて、タスクの戻り値の一部と見なされます。 submitで送信されたタスクが例外で終了した場合、 Future.get を呼び出すときに再スローされ、ExecutionExceptionにラップされます。

12
Soundlink

はるかに優れたソリューション: Java FutureTask完了チェック

futureTask.get()を呼び出して計算結果を取得すると、基になるRunnable/Callableが例外をスローした場合、例外(ExecutionException)がスローされます。

ExecutionException.getCause()は、Runnable/Callableがスローした例外を返します。

Runnable/Callableがキャンセルされた場合も、別の例外がスローされます。

FutureTaskのソースコードを調べましたが、setExceptionが呼び出されている場所が見つかりませんでした。
innerSetExceptionがスローされた場合に呼び出されるFutureTask.SyncFutureTaskの内部クラス)からのThrowableメソッドがあります。 runメソッド。このメソッドはsetExceptionでも呼び出されています。
つまり、javadocが正しくない(または理解するのが非常に難しい)ように見えます。

2
user85421

3つの標準的な方法と1つの即興の方法があります。 1. UncaughtExceptionHandlerを使用して、作成されたスレッドのUncaughtExceptionHandlerを次のように設定します。

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            public void uncaughtException(Thread t, Throwable ex) {..}}

*ただし、スレッドによってスローされた例外をキャッチするという制限がありますが、将来のタスクの場合は飲み込まれます。 2.この目的のために特別に提供されたフック付きのカスタムthreadpoolexecutorを作成した後、afterExecuteを使用します。 ThreadpoolExecutorのコードを調べて、submit> executeを使用します(workQueueがあります。workQueue.offer)、タスクがワークキューに追加されます

   final void runWorker(Worker arg0) {
  Thread arg1 = Thread.currentThread();
  Runnable arg2 = arg0.firstTask;
  ..
     while(arg2 != null || (arg2 = this.**getTask()**) != null) {
        arg0.lock();
        ..
        try {
           this.beforeExecute(arg1, arg2);
           Object arg4 = null;
           try {
              arg2.run();
           } catch (RuntimeException arg27) {
             ..
           } finally {
              this.**afterExecute**(arg2, (Throwable)arg4);
           }

  }

getTask() {..
 this.workQueue.**poll**();
..}
  1. 次に、3番目はcallメソッド内で単純なtry catchを使用していますが、ここの外では例外をキャッチできません。

  2. 回避策は、呼び出し可能オブジェクトを解放するファクトリであるTaskFactoryの呼び出しメソッドからすべての呼び出しメソッドを呼び出すことです。

0
hi.nitish