如何从Executors正确捕获RuntimeExceptions?
说我有以下代码:
ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(myRunnable);
现在,如果myRunnable
抛出一个RuntimeExcpetion
,我怎么能抓住它? 一种方法是将我自己的ThreadFactory
实现提供给newSingleThreadExecutor()
并为其中的newSingleThreadExecutor()
设置自定义的uncaughtExceptionHandler
。 另一种方法是将myRunnable
包装为包含try-catch- myRunnable
的本地(匿名) Runnable
。 也许还有其他类似的解决方法。 但是…不知怎的,这感觉很脏,我觉得它不应该是这么复杂。 有一个干净的解决scheme吗?
干净的解决方法是使用ExecutorService.submit()
而不是execute()
。 这将返回一个Future
,您可以使用它来检索任务的结果或exception:
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(); }
为什么不调用ExecutorService#submit()
,在调用Future#get()
时候,把Future
放回去,然后自己处理可能的exception呢?
在另一个可捕获运行时exception并处理它们的runnable中装饰可运行的:
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));
skaffman是正确的,使用submit
是最干净的方法。 另一种方法是afterExecute(Runnable, Throwable)
ThreadPoolExecutor
并覆盖afterExecute(Runnable, Throwable)
。 如果你遵循这个方法, 一定要调用execute(Runnable)
而不是submit(Runnable)
否则afterExecute
将不会被调用。
根据API说明:
在完成给定Runnable的执行后调用的方法。 该方法由执行任务的线程调用。 如果非null,则Throwable是未捕获的
RuntimeException
或导致执行突然终止的Error
。注意:当任务(例如FutureTask)中明确地或通过诸如submit之类的方法将操作包含在任务中时,这些任务对象捕获和维护计算exception,因此它们不会导致突然终止,并且内部exception不会传递给此方法 。
提交给ThreadPoolExecutors
的任务( Callable
或Runnable
)将被转换为FuturnTask
,包含一个名为callable
的道具等于您提交的任务。 FuturnTask有自己的run
方法如下。 所有抛出c.call()
exception或抛出将被c.call()
并放入一个名为outcome
的道具。 当调用FuturnTask的get
方法时, outcome
将被抛出
FuturnTask.run从Jdk1.8源代码
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. skaffman的回答
- 2.当您新build一个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 } }