如何在FutureTask中捕获exception

在发现在Java 1.6(和Eclipse Executors.newCachedThreadPool()上的Executors.newCachedThreadPool()运行的FutureTask吞下了Runnable.run()方法中的exception后,我试图找到一种方法来捕获这些exception而不添加throw / catch我所有的Runnable实现。

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

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

但是,似乎没有调用此方法(使用调试器运行显示FutureTask捕获exception,但未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 { public MyFutureTask(Runnable r) { super(r, null); } @Override protected void setException(Throwable t) { super.setException(t); System.out.println("Exception: " + t); } } 

我的主要问题是:如何捕获FutureTask中引发的exception? 为什么不调用setException

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

setException可能不是用于覆盖,而是用于在需要时将结果设置为exception。 你想要做的是覆盖done()方法并尝试获得结果:

 public class MyFutureTask extends FutureTask { 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); } } } 

您是否尝试过使用UncaughtExceptionHandler

  • 您需要实现UncaughtExceptionHandler接口。
  • 要为池线程设置UncaughtExceptionHandler ,请在Executor.newCachedThreadPool(ThreadFactory)调用中提供ThreadFactory
  • 您可以通过setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)为创建的线程设置UncaughtExceptionHandler

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

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

当调用futureTask.get()来检索计算结果时,如果底层Runnable / Callable引发exception,它将抛出exception(ExecutionException)。

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

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

我查看了FutureTask的源代码,但无法找到调用setException位置。
FutureTask.SyncFutureTask.Sync内部类)中有一个innerSetException方法,在run方法抛出Throwable情况下调用该方法。 此方法也在setExceptionsetException
所以像javadoc这样的接缝不正确(或者很难理解……)。

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

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

*但是限制是它捕获线程抛出的exception,但是在未来任务的情况下,它被吞噬。 2.使用专门为此目的提供的钩子制作自定义threadpoolexecutor后使用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但是你不能在这里捕获exception。

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