代码之家  ›  专栏  ›  技术社区  ›  Thirler

如何在FutureTask中捕获异常

  •  20
  • Thirler  · 技术社区  · 16 年前

    在发现之后 FutureTask 跑进 Executors.newCachedThreadPool() Java 1.6(和Eclipse)中的吞咽异常 Runnable.run() 方法,我试图找到一种方法来捕获这些,而不向所有的 Runnable 实施。

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

    使此未来报告一个以给定的可丢弃项为其原因的ExecutionException,除非此未来已设置或已取消。当计算失败时,run方法会在内部调用此方法。

    但是,似乎没有调用此方法(使用调试器运行时,会显示异常被 未来任务 但是 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中抛出的异常?为什么不 设置异常 打电话来?

    我也想知道为什么 Thread.UncaughtExceptionHandler 机制未被使用 未来任务 有什么理由吗?

    5 回复  |  直到 8 年前
        1
  •  20
  •   gustafc    16 年前

    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);
            }
        }
    }
    
        2
  •  12
  •   Soundlink    16 年前

    你试过用 UncaughtExceptionHandler ?

    • 你需要实现 未捕获的异常处理程序 接口。
    • 设置一个 未捕获的异常处理程序 对于池线程,请提供 ThreadFactory Executor.newCachedThreadPool(ThreadFactory) 打电话。
    • 可以通过以下方式为创建的线程设置未捕获的异常处理程序: setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

    使用提交任务 ExecutorService.execute ,因为只有从与提交的任务中引发的异常 execute 使其成为未捕获的异常处理程序。用于提交的任务 ExecutorService.submit 任何抛出的异常都被认为是任务返回值的一部分。如果用Submit提交的任务因异常终止,则在调用 Future.get 包裹在一个 ExecutionException

        3
  •  9
  •   Community Mohan Dere    9 年前

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

    当你打电话 futureTask.get() 为了检索计算结果,如果底层 Runnable / Callable 引发异常。

    ExecutionException.getCause() 将返回异常 可运行的 / 可赎回的 投掷。

    如果 可运行的 / 可赎回的 被取消了。

        4
  •  2
  •   user85421    16 年前

    我已经看过的源代码 FutureTask 却找不到 setException 正在被呼叫。
    有一个 innerSetException 方法从 FutureTask.Sync (内部类) 未来任务 )如果发生 Throwable 被run方法抛出。此方法也在 设置异常 .
    所以它的接缝就像javadoc是不正确的(或者很难理解…)。

        5
  •  0
  •   hi.nitish    8 年前

    有三种标准方法和一种简易方法。 1。使用uncaughtexceptionhandler,将所创建线程的uncaughtexceptionhandler设置为

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

    *但限制是它捕获线程抛出的异常,但在将来的任务中,它会被吞没。 2。使用 afterExecute 在制作了一个具有专门为此目的提供的钩子的自定义线程池执行器之后。通过Submit>Execute(有一个工作队列, 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是一个释放可调用文件的工厂。