java 如何从 Executors 正确捕获 RuntimeExceptions?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1687977/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 17:36:48  来源:igfitidea点击:

How to properly catch RuntimeExceptions from Executors?

javaconcurrencyexecutorruntimeexception

提问by Joonas Pulakka

Say that I have the following code:

假设我有以下代码:

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

Now, if myRunnablethrows a RuntimeExcpetion, how can I catch it? One way would be to supply my own ThreadFactoryimplementation to newSingleThreadExecutor()and set custom uncaughtExceptionHandlers for the Threads that come out of it. Another way would be to wrap myRunnableto a local (anonymous) Runnablethat contains a try-catch -block. Maybe there are other similar workarounds too. But... somehow this feels dirty, I feel that it shouldn't be this complicated. Is there a clean solution?

现在,如果myRunnable抛出 a RuntimeExcpetion,我怎么能抓住它?一种方法是提供我自己的ThreadFactory实现newSingleThreadExecutor()uncaughtExceptionHandler为从中产生的Threads设置自定义s 。另一种方法是包装myRunnableRunnable包含 try-catch 块的本地(匿名)。也许还有其他类似的解决方法。但是……不知怎的,这感觉很脏,我觉得不应该这么复杂。有干净的解决方案吗?

回答by skaffman

The clean workaround is to use ExecutorService.submit()instead of execute(). This returns you a Futurewhich you can use to retrieve the result or exception of the task:

干净的解决方法是使用ExecutorService.submit()而不是execute(). 这将返回一个Future可用于检索任务结果或异常的 a:

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();
}

回答by Aaron Digulla

Decorate the runnable in another runnable which catches the runtime exceptions and handles them:

在另一个可捕获运行时异常并处理它们的可运行对象中装饰可运行对象:

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));

回答by Brian Agnew

Why not call ExecutorService#submit(), get the Futureback and then handle possible exceptions yourself when calling Future#get()?

为什么不调用ExecutorService#submit(),获取Future返回然后在调用时自己处理可能的异常Future#get()

回答by Adamski

skaffman is correct in that using submitis the cleanest approach. An alternative approach is to subclass ThreadPoolExecutorand override afterExecute(Runnable, Throwable). If you follow this approach be sure to call execute(Runnable)rather than submit(Runnable)or afterExecutewill not be invoked.

skaffman 是正确的,因为使用submit是最干净的方法。另一种方法是子类化ThreadPoolExecutor和覆盖afterExecute(Runnable, Throwable)。如果您遵循这种方法,请确保调用execute(Runnable)而不是submit(Runnable)afterExecute不会被调用。

Per the API description:

根据 API 描述:

Method invoked upon completion of execution of the given Runnable. This method is invoked by the thread that executed the task. If non-null, the Throwable is the uncaught RuntimeExceptionor Errorthat caused execution to terminate abruptly.

Note: When actions are enclosed in tasks (such as FutureTask) either explicitly or via methods such as submit, these task objects catch and maintain computational exceptions, and so they do not cause abrupt termination, and the internal exceptions are not passed to this method.

在给定 Runnable 的执行完成后调用的方法。该方法由执行任务的线程调用。如果非空,则 Throwable 是未捕获的 RuntimeExceptionError导致执行突然终止的。

注意:当Action显式或通过submit等方法包含在task(如FutureTask)中时,这些task对象会捕获并维护计算异常,因此不会导致突然终止,内部异常不会传递给该方法.

回答by gilon chiu

a task(Callableor Runnable) submitted to ThreadPoolExecutorswill be convert to a FuturnTask, contains a prop named callableequals the task you submit. FuturnTask has its own runmethod as follows. All exception or throwable throwed in c.call()will be catched and put into a prop named outcome. When calling FuturnTask's getmethod, outcomewill be throwed

提交给的任务(CallableRunnableThreadPoolExecutors将转换为FuturnTask,包含一个名为callable等于您提交的任务的道具。FuturnTask 有自己的run方法如下。抛出的所有异常或 throwable 都c.call()将被捕获​​并放入名为outcome. 调用 FuturnTask 的get方法时,outcome会抛出

FuturnTask.run From Jdk1.8 Source Code

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);
            }
        }
        ...
    }

if you want catch the exception :

如果你想捕捉异常:

      1. skaffman's answer
      2. overwrite `afterExecute` when you new a ThreadPoolExecutor
      1. 斯卡夫曼的回答
      2. 当你新建一个 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
            }
        }