web-dev-qa-db-ja.com

エグゼキューターからのRuntimeExceptionを適切にキャッチする方法は?

次のコードがあるとします。

_ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);
_

myRunnableRuntimeExcpetionをスローした場合、どうすればそれをキャッチできますか? 1つの方法は、独自のThreadFactory実装をnewSingleThreadExecutor()に提供し、そこから取得するuncaughtExceptionHandlersにカスタムThreadsを設定することです。別の方法は、try-catch -blockを含むローカル(匿名)myRunnableRunnableをラップすることです。多分他にも同様の回避策があります。でも……なんとなく汚い感じがするので、こんなに複雑になってはいけないと思います。クリーンなソリューションはありますか?

43
Joonas Pulakka

クリーンな回避策は、ExecutorService.submit()の代わりにexecute()を使用することです。これはFutureを返します。これを使用して、タスクの結果または例外を取得できます。

ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable task = new Runnable() {
  public void run() {
    throw new RuntimeException("foo");
  }
};

Future<?> future = executor.submit(task);
try {
  future.get();
} catch (ExecutionException e) {
  Exception rootException = e.getCause();
}
56
skaffman

ランタイム例外をキャッチして処理する別のランナブルでランナブルを装飾します。

public class REHandler implements Runnable {
    Runnable delegate;
    public REHandler (Runnable delegate) {
        this.delegate = delegate;
    }
    public void run () {
        try {
            delegate.run ();
        } catch (RuntimeException e) {
            ... your fancy error handling here ...
        }
    }
}

executor.execute(new REHandler (myRunnable));
10
Aaron Digulla

なぜ ExecutorService#submit() を呼び出さないでください Future を取得し、次に Future#get()

9
Brian Agnew

skaffmanは、submitを使用するのが最もクリーンな方法であるという点で正しいです。別のアプローチは、サブクラス ThreadPoolExecutor をオーバーライドし、afterExecute(Runnable, Throwable)をオーバーライドすることです。このアプローチを実行する場合は、execute(Runnable)またはafterExecuteではなく、必ずsubmit(Runnable)を呼び出してください。呼び出されません。

APIの説明に従って:

指定されたRunnableの実行が完了すると呼び出されるメソッド。このメソッドは、タスクを実行したスレッドによって呼び出されます。 null以外の場合、ThrowableはキャッチされていないRuntimeExceptionまたはErrorです。 実行が突然終了しました。

注:アクションが明示的にまたは送信などのメソッドを介してタスク(FutureTaskなど)に囲まれている場合、これらのタスクオブジェクトは計算例外をキャッチして維持するため、突然終了することはなく、内部例外はこのメソッドに渡されません

6
Adamski

Callableに送信されたタスク(RunnableまたはThreadPoolExecutors)は、FuturnTaskに変換され、 callable 送信したタスクと同じです。 FuturnTaskには、次のような独自のrunメソッドがあります。 c.call()でスローされたすべての例外またはスロー可能オブジェクトはキャッチされ、outcomeという名前のプロップに入れられます。 FuturnTaskのgetメソッドを呼び出すと、outcomeがスローされます

Jdk1.8ソースコードからのFuturnTask.run

public void run() {
        ...
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    // save ex into `outcome` prop
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        }
        ...
    }

例外をキャッチしたい場合:

      1.スカフマンの答え
      2. ThreadPoolExecutorを新規作成するときに「afterExecute」を上書きします
        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            super.afterExecute(r, t);
            Throwable cause = null;
            if (t == null && r instanceof Future) {
                try {
                    ((Future<?>) r).get();
                } catch (InterruptedException | ExecutionException e) {
                    cause = e;
                }
            } else if (t != null) {
                cause = t;
            }
            if (cause != null) {
                // log error
            }
        }
2
gilon chiu