次のコードがあるとします。
_ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);
_
myRunnable
がRuntimeExcpetion
をスローした場合、どうすればそれをキャッチできますか? 1つの方法は、独自のThreadFactory
実装をnewSingleThreadExecutor()
に提供し、そこから取得するuncaughtExceptionHandler
sにカスタムThread
sを設定することです。別の方法は、try-catch -blockを含むローカル(匿名)myRunnable
にRunnable
をラップすることです。多分他にも同様の回避策があります。でも……なんとなく汚い感じがするので、こんなに複雑になってはいけないと思います。クリーンなソリューションはありますか?
クリーンな回避策は、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();
}
ランタイム例外をキャッチして処理する別のランナブルでランナブルを装飾します。
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));
なぜ ExecutorService#submit()
を呼び出さないでください Future
を取得し、次に Future#get()
?
skaffmanは、submit
を使用するのが最もクリーンな方法であるという点で正しいです。別のアプローチは、サブクラス ThreadPoolExecutor
をオーバーライドし、afterExecute(Runnable, Throwable)
をオーバーライドすることです。このアプローチを実行する場合は、execute(Runnable)
またはafterExecute
ではなく、必ずsubmit(Runnable)
を呼び出してください。呼び出されません。
APIの説明に従って:
指定されたRunnableの実行が完了すると呼び出されるメソッド。このメソッドは、タスクを実行したスレッドによって呼び出されます。 null以外の場合、Throwableはキャッチされていない
RuntimeException
またはError
です。 実行が突然終了しました。注:アクションが明示的にまたは送信などのメソッドを介してタスク(FutureTaskなど)に囲まれている場合、これらのタスクオブジェクトは計算例外をキャッチして維持するため、突然終了することはなく、内部例外はこのメソッドに渡されません。
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);
}
}
...
}
例外をキャッチしたい場合:
@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
}
}