23

在发现在 Java 1.6(以及来自 Eclipse)中FutureTask运行会吞下方法中的异常后,我试图想出一种方法来捕获这些异常,而无需在我的所有实现中添加 throw/catch。Executors.newCachedThreadPool()Runnable.run()Runnable

API 建议重写FutureTask.setException()应该对此有所帮助:

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

然而,这个方法似乎没有被调用(使用调试器运行显示异常被 捕获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);
    }
}

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

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

4

5 回答 5

21

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);
        }
    }
}
于 2010-08-24T12:23:40.273 回答
12

您是否尝试过使用UncaughtExceptionHandler?

  • 您需要实现UncaughtExceptionHandler接口。
  • UncaughtExceptionHandler为池线程设置一个,请ThreadFactoryExecutor.newCachedThreadPool(ThreadFactory)调用中提供一个。
  • 您可以通过以下方式为创建的线程设置 UncaughtExceptionHandlersetUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

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

于 2010-08-24T12:33:06.637 回答
9

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

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

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

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

于 2013-03-31T18:01:47.260 回答
2

我查看了源代码,FutureTask但找不到setException调用的位置。
在run 方法抛出的情况下,将调用(内部类)中的一个innerSetException方法。这个方法也在被调用。 所以看起来javadoc是不正确的(或者很难理解......)。FutureTask.SyncFutureTaskThrowablesetException

于 2010-08-24T11:47:16.697 回答
0

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

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

*但限制是它捕获线程抛出的异常,但在未来任务的情况下,它会被吞噬。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. 然后,第三个是在调用方法内部使用简单的try catch,但您无法在此处捕获外部异常。

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

于 2018-05-23T05:04:58.900 回答