Java 如何在 FutureTask 中捕获异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3555302/
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-08-14 01:49:18  来源:igfitidea点击:

How to catch exceptions in FutureTask

javaconcurrencyexecutorservicefuturetask

提问by Thirler

After finding that FutureTaskrunning in a Executors.newCachedThreadPool()on Java 1.6 (and from Eclipse) swallows exceptions in the Runnable.run()method, I've tried to come up with a way to catch these without adding throw/catch to all my Runnableimplementations.

发现后FutureTask在运行Executors.newCachedThreadPool()于Java 1.6(和Eclipse的)燕子在异常Runnable.run()的方法,我试图想出一个方法来捕捉这些不增加投掷/捕获我所有的Runnable实现。

The API suggests that overriding FutureTask.setException()should help in this:

API 建议覆盖FutureTask.setException()应该有助于:

Causes this future to report an ExecutionException with the given throwable as its cause, unless this Future has already been set or has been cancelled. This method is invoked internally by the run method upon failure of the computation.

导致此 Future 报告一个 ExecutionException 以给定的 throwable 作为其原因,除非此 Future 已被设置或已被取消。该方法在计算失败时由 run 方法在内部调用。

However this method doesn't seem to be called (running with the debugger shows the exception is caught by FutureTask, but setExceptionisn't called). I've written the following program to reproduce my problem:

但是,似乎没有调用此方法(使用调试器运行显示异常被 捕获FutureTask,但未setException调用)。我编写了以下程序来重现我的问题:

public class RunTest {
    public static void main(String[] args) {
        MyFutureTask t = new MyFutureTask(new Runnable() {

            @Override
            public void run() {
                throw new RuntimeException("Unchecked exception");

            }
        });

        ExecutorService service = Executors.newCachedThreadPool();
        service.submit(t);
    }
}

public class MyFutureTask extends FutureTask<Object> {

    public MyFutureTask(Runnable r) {
        super(r, null);
    }

    @Override
    protected void setException(Throwable t) {
        super.setException(t);
        System.out.println("Exception: " + t);
    }
}

My main question is: How can I catch Exceptions thrown in a FutureTask? Why doesn't setExceptionget called?

我的主要问题是:如何捕获 FutureTask 中抛出的异常?为什么不setException叫?

Also I would like to know why the Thread.UncaughtExceptionHandlermechanism isn't used by FutureTask, is there any reason for this?

另外我想知道为什么Thread.UncaughtExceptionHandler不使用该机制FutureTask,有什么原因吗?

采纳答案by gustafc

setExceptionprobably isn't made for overriding, but is provided to let you set the result to an exception, should the need arise. What you want to do is override the done()method and try to get the result:

setException可能不是为了覆盖而设计的,而是为了让您在需要时将结果设置为异常。您想要做的是覆盖该done()方法并尝试获取结果:

public class MyFutureTask extends FutureTask<Object> {

    public MyFutureTask(Runnable r) {
        super(r, null);
    }

    @Override
    protected void done() {
        try {
            if (!isCancelled()) get();
        } catch (ExecutionException e) {
            // Exception occurred, deal with it
            System.out.println("Exception: " + e.getCause());
        } catch (InterruptedException e) {
            // Shouldn't happen, we're invoked when computation is finished
            throw new AssertionError(e);
        }
    }
}

回答by user85421

I have looked at the source code of FutureTaskand could not find where setExceptionis being called.
There is an innerSetExceptionmethod from FutureTask.Sync(inner class of FutureTask) that is being called in case of an Throwablebeing thrown by the run method. This method is also being called in setException.
So it seams like the javadoc is not correct (or very hard to understand...).

我查看了 的源代码,FutureTask但找不到setException被调用的位置。
有一个innerSetException来自FutureTask.Sync(内部类FutureTask)的方法被调用,以防Throwable被 run 方法抛出。中也调用了此方法setException
所以它看起来像javadoc不正确(或者很难理解......)。

回答by Soundlink

Have you tried using an UncaughtExceptionHandler?

您是否尝试过使用UncaughtExceptionHandler?

  • You need to implement the UncaughtExceptionHandlerinterface.
  • To set an UncaughtExceptionHandlerfor pool threads, provide a ThreadFactoryin the Executor.newCachedThreadPool(ThreadFactory)call.
  • You can set the UncaughtExceptionHandler for the created thread via setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
  • 您需要实现该UncaughtExceptionHandler接口。
  • UncaughtExceptionHandler为池线程设置一个,请ThreadFactoryExecutor.newCachedThreadPool(ThreadFactory)调用中提供一个。
  • 您可以通过以下方式为创建的线程设置 UncaughtExceptionHandler setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

Submit the tasks with ExecutorService.execute, because only exceptions thrown from tasks submitted with executemake it to the uncaught exception handler. For Tasks submitted with ExecutorService.submitany thrown exception is considered to be part of the task's return value. If a task submitted with submit terminates with an exception, it is rethrown when calling Future.get, wrapped in an ExecutionException

用 提交任务ExecutorService.execute,因为只有从提交的任务中抛出的异常execute才会进入未捕获的异常处理程序。对于ExecutorService.submit任何抛出异常提交的任务,都被视为任务返回值的一部分。如果使用 submit 提交的任务因异常终止,则在调用时重新抛出Future.get,包装在ExecutionException

回答by Christopher Alexander

A much better solution: Java FutureTask completion check

一个更好的解决方案: Java FutureTask 完成检查

When you call futureTask.get()to retrieve the result of the computation it will throw an exception (ExecutionException) if the underlying Runnable/Callablethrew an exception.

当您调用futureTask.get()以检索计算结果时,如果底层Runnable/Callable抛出异常,它将抛出异常 (ExecutionException) 。

ExecutionException.getCause()will return the exception that the Runnable/Callablethrew.

ExecutionException.getCause()将返回Runnable/Callable抛出的异常。

It will also throw a different exception if the Runnable/Callablewas canceled.

如果Runnable/Callable被取消,它也会抛出一个不同的异常。

回答by hi.nitish

There are three standard ways and one improvised way. 1. use UncaughtExceptionHandler, set the UncaughtExceptionHandler for the created thread as

共有三种标准方式和一种即兴方式。1.使用UncaughtExceptionHandler,将创建的线程的UncaughtExceptionHandler设置为

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            public void uncaughtException(Thread t, Throwable ex) {..}}

*But the limitation is it catches the exception thrown by thread but in case of future task, it is swallowed. 2. use afterExecuteafter making a custom threadpoolexecutor with hook that has been provided specially for this purpose. Looking through the code of ThreadpoolExecutor, via submit > execute (there is a workQueue, workQueue.offer), the tasks are added to the work queue

*但限制是它捕获线程抛出的异常,但在未来任务的情况下,它被吞掉。2.使用afterExecute专门为此提供的带有钩子的自定义线程池执行器后使用。查看ThreadpoolExecutor的代码,通过submit > execute(有一个workQueue, workQueue.offer),将任务加入到工作队列中

   final void runWorker(Worker arg0) {
  Thread arg1 = Thread.currentThread();
  Runnable arg2 = arg0.firstTask;
  ..
     while(arg2 != null || (arg2 = this.**getTask()**) != null) {
        arg0.lock();
        ..
        try {
           this.beforeExecute(arg1, arg2);
           Object arg4 = null;
           try {
              arg2.run();
           } catch (RuntimeException arg27) {
             ..
           } finally {
              this.**afterExecute**(arg2, (Throwable)arg4);
           }

  }

getTask() {..
 this.workQueue.**poll**();
..}
  1. Then, the third is using simple try catch inside the call method but you can not catch the exception outside here.

  2. The workaround is calling all the call methods from a call method of a TaskFactory, a factory that releases callables.

  1. 然后,第三个是在 call 方法内部使用简单的 try catch ,但您不能在这里捕获异常。

  2. 解决方法是从 TaskFactory 的调用方法调用所有调用方法,TaskFactory 是一个释放可调用对象的工厂。