如何正确捕获 Executor 中的 RuntimeException?
-
18-09-2019 - |
题
假设我有以下代码:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);
现在,如果 myRunnable
抛出一个 RuntimeExcpetion
, ,我怎样才能抓住它?一种方法是提供我自己的 ThreadFactory
实施到 newSingleThreadExecutor()
并设置自定义 uncaughtExceptionHandler
s 为 Thread
是从中出来的。另一种方法是包裹 myRunnable
给本地人(匿名) Runnable
包含一个 try-catch 块。也许还有其他类似的解决方法。但...不知怎的,这感觉很脏,我觉得它不应该这么复杂。有干净的解决方案吗?
解决方案
在清洁解决方法是使用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)
而非submit(Runnable)
或afterExecute
将不会被调用。
每API的描述:
方法完成时调用 在给定的Runnable的执行。这个 方法由该线程调用该 执行任务。如果非空,在 Throwable的是未被捕获 的
RuntimeException
强>或Error
强>引起 执行突然终止。注意:当操作被封闭在 任务(如FutureTask)任一 明确地或通过方法如 提交,这些任务对象捕捉和 维护计算异常, 所以它们不会导致突然 终止,并且内部 例外的不传递给此 方法强>
任务(Callable
或 Runnable
)提交 ThreadPoolExecutors
将被转换成 FuturnTask
, 包含一个道具命名的 callable
就等于你的任务提交。FuturnTask有其自己的 run
方法如下。所有异常或抛出扔在 c.call()
将被逮住,并投入的支柱名为 outcome
.打电话时FuturnTask的 get
方法, outcome
会倒掉
FuturnTask.从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.复盖`afterExecute`当你新的一ThreadPoolExecutor
@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
}
}