如何在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
您是否尝试过使用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.Sync
( FutureTask.Sync
内部类)中有一个innerSetException
方法,在run方法抛出Throwable
情况下调用该方法。 此方法也在setException
中setException
。
所以像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**(); ..}
-
然后,第三个是在调用方法中使用简单的try catch但是你不能在这里捕获exception。
-
解决方法是从TaskFactory的调用方法调用所有调用方法,TaskFactory是一个释放callables的工厂。