Java 如何正确捕获执行器的运行时异常?

Java 如何正确捕获执行器的运行时异常?,java,concurrency,executor,runtimeexception,Java,Concurrency,Executor,Runtimeexception,假设我有以下代码: ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(myRunnable); 现在,如果myRunnable抛出一个runtimeexpetion,我如何捕获它?一种方法是为newSingleThreadExecutor()提供我自己的ThreadFactory实现,并为由此产生的线程设置自定义uncaughtExceptionHandlers。另一种方法是将myR

假设我有以下代码:

ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);

现在,如果
myRunnable
抛出一个
runtimeexpetion
,我如何捕获它?一种方法是为
newSingleThreadExecutor()
提供我自己的
ThreadFactory
实现,并为由此产生的
线程设置自定义
uncaughtExceptionHandler
s。另一种方法是将
myRunnable
包装到包含try-catch-block的本地(匿名)
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();
}
ExecutorService executor=Executors.newSingleThreadExecutor();
Runnable任务=新的Runnable(){
公开募捐{
抛出新的运行时异常(“foo”);
}
};
未来=执行者提交(任务);
试一试{
future.get();
}捕获(执行例外){
异常rootException=e.getCause();
}

skaffman是正确的,因为使用
submit
是最干净的方法。另一种方法是子类化并重写afterExecute(Runnable,Throwable)
。如果您采用这种方法,请确保调用
execute(Runnable)
而不是
submit(Runnable)
afterExecute

根据API说明:

完成时调用的方法 执行给定的Runnable。这 方法由 执行任务。如果非null,则 可丢弃的是未捕获的
运行时异常
或导致 行刑突然终止

注意:当动作包含在 任务(如FutureTask)或 显式地或通过诸如 提交时,这些任务对象捕获并 维护计算异常,以及 因此,它们不会导致突然的 终止,以及内部 异常未传递给此 方法


在另一个runnable中修饰runnable,该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));
提交给
ThreadPoolExecutors
的任务(
Callable
Runnable
)将转换为
FuturnTask
,包含一个名为
Callable
的属性,该属性等于您提交的任务。FuturnTask有自己的
run
方法,如下所示。在
c.call()
中抛出的所有异常或可丢弃项将被捕获并放入名为
output
的道具中。调用FuturnTask的
get
方法时,将抛出
output

从Jdk1.8源代码运行futurnstask.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);
            }
        }
        ...
    }
public void run(){
...
试一试{
可调用c=可调用;
如果(c!=null&&state==NEW){
V结果;
布尔ran;
试一试{
结果=c.call();
ran=真;
}捕获(可丢弃的ex){
结果=空;
ran=假;
//将ex保存到“结果”道具中
setException(ex);
}
如果(运行)
设置(结果);
}
}
...
}
如果要捕获异常,请执行以下操作:

    1.斯卡夫曼的回答 2.新建ThreadPoolExecutor时覆盖'afterExecute'
@覆盖
执行后受保护的无效(可运行的r、可丢弃的t){
super.afterExecute(r,t);
可丢弃原因=空;
if(t==null&&r未来实例){
试一试{
((Future)r.get();
}捕获(中断异常|执行异常e){
原因=e;
}
}else如果(t!=null){
原因=t;
}
如果(原因!=null){
//日志错误
}
}

老实说,我质疑捕获在不同线程中抛出的异常的意义。当前线程是否必须
加入该线程并等待抛出异常?您在问题中没有提到这一点。@BalusC:将异常从工作线程编组回调用线程是许多应用程序的常见要求。例如,UI应用程序可以调用SwingWorker线程来进行一些后台处理。如果处理失败,则需要将异常传递回事件分派线程。这是一个常见要求。线程1生成一些工作,通过线程2执行,但需要了解它是否成功(即引发异常)。Executor框架可以帮你解决这个问题。嗯,事实上,我还没有想到这一点。我只是好奇,总的来说,如何处理这个问题。但是人们似乎对下面的
submit()
Future
有一些很酷的话要说:-)谢谢,看起来和预期的一模一样。清除。此外,您可能希望使用
Callable
而不是
Runnable
,然后您的任务可以抛出选中的异常以及未选中的异常。getCause在1.6和1.7中返回一个Throwable而不是异常问题在于future.get()正在阻塞,因此,如果您希望能够异步运行任务,@Loic没有对其进行注释,那么此解决方案没有任何用处,因为它首先破坏了使用执行器的整个目的。
        @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
            }
        }