首页 文章

如何在FutureTask中捕获异常

提问于
浏览
20

在发现 Executors.newCachedThreadPool() 在Java 1.6上运行 Executors.newCachedThreadPool() (以及从Eclipse中)吞下 Runnable.run() 方法中的异常之后,我试图想出一种方法来捕获这些但不向我的所有 Runnable 实现添加throw / catch .

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

使此未来报告以给定throwable为原因的ExecutionException,除非已设置或已取消此Future . 在计算失败时,run方法在内部调用此方法 .

但是这个方法似乎没有被调用(运行调试器显示异常被 FutureTask 捕获,但 setException isn 't called). I'已写入以下程序来重现我的问题:

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 使用,这有什么理由吗?

5 回答

  • 9

    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);
            }
        }
    }
    
  • 0

    你尝试过使用UncaughtExceptionHandler吗?

    • 您需要实现UncaughtExceptionHandler接口 .

    • 要为池线程设置 UncaughtExceptionHandler ,请在 Executor.newCachedThreadPool(ThreadFactory) 调用中提供ThreadFactory .

    • 您可以通过 setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) 为创建的线程设置UncaughtExceptionHandler

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

  • 20

    一个更好的解决方案:Java FutureTask completion check

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

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

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

  • 2

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

  • 12

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

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

    *但是限制是它捕获线程抛出的异常,但是在未来任务的情况下,它被吞噬 . 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但是你不能在这里捕获异常 .

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

相关问题