JavaのThreadPoolExecutor
クラスを使用して、固定数のスレッドで多数の重いタスクを実行しようとしています。各タスクには、例外のために失敗する可能性がある多くの場所があります。
ThreadPoolExecutor
をサブクラス化し、タスクの実行中に発生したキャッチされない例外を提供するafterExecute
メソッドをオーバーライドしました。しかし、私はそれを機能させることができないようです。
例えば:
public class ThreadPoolErrors extends ThreadPoolExecutor {
public ThreadPoolErrors() {
super( 1, // core threads
1, // max threads
1, // timeout
TimeUnit.MINUTES, // timeout units
new LinkedBlockingQueue<Runnable>() // work queue
);
}
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if(t != null) {
System.out.println("Got an error: " + t);
} else {
System.out.println("Everything's fine--situation normal!");
}
}
public static void main( String [] args) {
ThreadPoolErrors threadPool = new ThreadPoolErrors();
threadPool.submit(
new Runnable() {
public void run() {
throw new RuntimeException("Ouch! Got an error.");
}
}
);
threadPool.shutdown();
}
}
このプログラムからの出力は、「すべて正常です-状況は正常です!」です。スレッドプールに送信された唯一のRunnableが例外をスローしても。ここで何が起こっているのか、手がかりはありますか?
ありがとう!
docs から:
注:アクションがタスク(FutureTaskなど)に明示的にまたはsubmitなどのメソッドを介して囲まれている場合、これらのタスクオブジェクトは計算例外をキャッチして維持するため、突然の終了を引き起こさず、内部例外はこのメソッドに渡されません。
Runnableを送信すると、それはFutureにラップされます。
AfterExecuteは次のようになります。
public final class ExtendedExecutor extends ThreadPoolExecutor {
// ...
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try {
Future<?> future = (Future<?>) r;
if (future.isDone()) {
future.get();
}
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
if (t != null) {
System.out.println(t);
}
}
}
WARNING:このソリューションは呼び出しスレッドをブロックすることに注意する必要があります。
タスクによってスローされた例外を処理する場合は、通常、Callable
よりもRunnable
を使用することをお勧めします。
Callable.call()
はチェック済み例外をスローすることが許可されており、これらは呼び出しスレッドに伝播されます。
Callable task = ...
Future future = executor.submit(task);
try {
future.get();
} catch (ExecutionException ex) {
ex.getCause().printStackTrace();
}
Callable.call()
が例外をスローした場合、これはExecutionException
にラップされ、Future.get()
によってスローされます。
これは、ThreadPoolExecutor
をサブクラス化するよりもはるかに望ましい方法です。また、例外が回復可能なものである場合、タスクを再送信する機会も与えられます。
この動作の説明は afterExecuteのjavadoc にあります。
注:アクションがタスク(FutureTaskなど)に明示的にまたはsubmitなどのメソッドを介して囲まれている場合、これらのタスクオブジェクトは計算例外をキャッチして維持するため、突然の終了を引き起こさず、内部例外はこのメソッドに渡されません。
Executorに送信された提供されたrunnableをラップすることで回避しました。
CompletableFuture.runAsync(
() -> {
try {
runnable.run();
} catch (Throwable e) {
Log.info(Concurrency.class, "runAsync", e);
}
},
executorService
);
VerboseRunnable
jcabi-log のクラスを使用しています。これはすべての例外を飲み込み、ログに記録します。非常に便利です、例えば:
import com.jcabi.log.VerboseRunnable;
scheduler.scheduleWithFixedDelay(
new VerboseRunnable(
Runnable() {
public void run() {
// the code, which may throw
}
},
true // it means that all exceptions will be swallowed and logged
),
1, 1, TimeUnit.MILLISECONDS
);
別の解決策は、ManagedTaskおよびManagedTaskListenerを使用することです。
インターフェースを実装するCallableまたはRunnableが必要ですManagedTask。
メソッドgetManagedTaskListener
は、必要なインスタンスを返します。
public ManagedTaskListener getManagedTaskListener() {
そして、ManagedTaskListenertaskDone
メソッドを実装します:
@Override
public void taskDone(Future<?> future, ManagedExecutorService executor, Object task, Throwable exception) {
if (exception != null) {
LOGGER.log(Level.SEVERE, exception.getMessage());
}
}
管理対象タスクのライフサイクルとリスナー の詳細。
タスクの実行を監視する場合は、1つまたは2つのスレッド(おそらく負荷に応じて)をスピンし、それらを使用してExecutionCompletionServiceラッパーからタスクを取得できます。
ExecutorService
が外部ソースからのものである場合(つまり、ThreadPoolExecutor
をサブクラス化してafterExecute()
をオーバーライドすることはできません)、動的プロキシを使用して目的の動作を実現できます。
public static ExecutorService errorAware(final ExecutorService executor) {
return (ExecutorService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class[] {ExecutorService.class},
(proxy, method, args) -> {
if (method.getName().equals("submit")) {
final Object arg0 = args[0];
if (arg0 instanceof Runnable) {
args[0] = new Runnable() {
@Override
public void run() {
final Runnable task = (Runnable) arg0;
try {
task.run();
if (task instanceof Future<?>) {
final Future<?> future = (Future<?>) task;
if (future.isDone()) {
try {
future.get();
} catch (final CancellationException ce) {
// Your error-handling code here
ce.printStackTrace();
} catch (final ExecutionException ee) {
// Your error-handling code here
ee.getCause().printStackTrace();
} catch (final InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
}
} catch (final RuntimeException re) {
// Your error-handling code here
re.printStackTrace();
throw re;
} catch (final Error e) {
// Your error-handling code here
e.printStackTrace();
throw e;
}
}
};
} else if (arg0 instanceof Callable<?>) {
args[0] = new Callable<Object>() {
@Override
public Object call() throws Exception {
final Callable<?> task = (Callable<?>) arg0;
try {
return task.call();
} catch (final Exception e) {
// Your error-handling code here
e.printStackTrace();
throw e;
} catch (final Error e) {
// Your error-handling code here
e.printStackTrace();
throw e;
}
}
};
}
}
return method.invoke(executor, args);
});
}
これは動作します
多くのタスクを取得できる単一のスレッドを持つExecutorを作成します。そして、現在のものが次を開始するために実行を終了するのを待ちます
不注意なエラーまたは例外の場合、ncaughtExceptionHandlerがキャッチします
public final class SingleThreadExecutorWithExceptions {[..____。] public static ExecutorService newSingleThreadExecutorWithExceptions(final Thread.UncaughtExceptionHandler uncaughtExceptionHandler){ ThreadFactory factory =(実行可能ランナブル)- > { final Thread newThread = new Thread(runnable、 "SingleThreadExecutorWithExceptions"); newThread.setUncaughtExceptionHandler((final Thread caugthThread、final Throwable throwable)-> { uncaughtExceptionHandler.uncaughtException (caugthThread、throwable); }); return newThread; }; return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1、1、 0L、TimeUnit.MILLISECONDS、 new LinkedBlockingQueue()、 factory){ prot ected void afterExecute(Runnable runnable、Throwable throwable){ super.afterExecute(runnable、throwable); if(throwable == null && runnable instanceof Future){ try { Future future =(Future)runnable; if(future.isDone()){ future.get(); } } catch (CancellationException ce){ throwable = ce; } catch(ExecutionException ee){ throwable = ee.getCause(); } catch(InterruptedException ie) { Thread.currentThread()。interrupt(); //無視/リセット } } if(throwable!= null){ uncaughtExceptionHandler.uncaughtException(Thread.currentThread()、throwable); } } }); } private static class FinalizableDelegatedExecutorService DelegatedExecutorServiceを拡張します{ FinalizableDelegatedExecutorService(ExecutorService executor){ super(executor); } protected void finalize(){ super .shutdown(); } } /** * ExecutorServiceメソッドのみを公開するラッパークラス * ExecutorService実装の。 */ private static class DelegatedExecutorServiceはAbstractExecutorServiceを拡張します{ private final ExecutorService e; DelegatedExecutorService(ExecutorService executor){e = executor; } public void execute(Runnable command){e.execute(command); } public void shutdown(){e.shutdown(); } public List shutdownNow(){return e.shutdownNow(); } public boolean isShutdown(){return e.isShutdown(); } public boolean isTerminated(){return e.isTerminated(); } public boolean awaitTermination(long timeout、TimeUnit unit) throws InterruptedException { return e.awaitTermination(timeout、unit); } public Future submit(Runnable task){ return e.submit(task); } public Future submit(Callable task){ return e.submit( task); } public Future submit(Runnable task、T result){ return e.submit(task、result); } public List> invokeAll(Collection> tasks) throws InterruptedException { return e.invokeAll(tasks); } public List> invokeAll(Collection> tasks 、 long timeout、TimeUnit unit) throws InterruptedException { return e.invokeAll(tasks、timeout、unit); } public T invokeAny(Collection> tasks) throws InterruptedException、ExecutionException { return e.invokeAny(tasks); } public T invokeAny(Collection> tasks、 long timeout、TimeUnit unit) throws InterruptedException、ExecutionException、TimeoutException { return e.invokeAny(tasks、timeout、unit); } } private SingleThreadExecutorWithExceptions(){} }
これは、次のようにAbstractExecutorService :: submit
がrunnable
をRunnableFuture
(FutureTask
以外)にラップしているためです。
AbstractExecutorService.Java
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null); /////////HERE////////
execute(ftask);
return ftask;
}
その後、execute
はそれをWorker
に渡し、Worker.run()
は以下を呼び出します。
ThreadPoolExecutor.Java
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run(); /////////HERE////////
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
最後に、上記のコード呼び出しの
task.run();
はFutureTask.run()
を呼び出します。例外ハンドラコードは次のとおりです。このため、予期される例外が発生していません。
class FutureTask<V> implements RunnableFuture<V>
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) { /////////HERE////////
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}