简体   繁体   English

如何在FutureTask中捕获异常

[英]How to catch exceptions in FutureTask

After finding that FutureTask running in a Executors.newCachedThreadPool() on Java 1.6 (and from Eclipse) swallows exceptions in the Runnable.run() method, I've tried to come up with a way to catch these without adding throw/catch to all my Runnable implementations. 在发现在Java 1.6(和Eclipse Executors.newCachedThreadPool()上的Executors.newCachedThreadPool()运行的FutureTask吞噬了Runnable.run()方法中的异常之后,我试图想出一种方法来捕获这些异常而不添加throw / catch我所有的Runnable实现。

The API suggests that overriding FutureTask.setException() should help in this: API建议覆盖FutureTask.setException()应该有助于:

Causes this future to report an ExecutionException with the given throwable as its cause, unless this Future has already been set or has been cancelled. 导致此未来报告ExecutionException,并将给定的throwable作为其原因,除非已设置或已取消此Future。 This method is invoked internally by the run method upon failure of the computation. 在计算失败时,run方法在内部调用此方法。

However this method doesn't seem to be called (running with the debugger shows the exception is caught by FutureTask , but setException isn't called). 但是,似乎没有调用此方法(使用调试器运行显示FutureTask捕获异常,但未setException )。 I've written the following program to reproduce my problem: 我写了以下程序来重现我的问题:

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

My main question is: How can I catch Exceptions thrown in a FutureTask? 我的主要问题是:如何捕获FutureTask中引发的异常? Why doesn't setException get called? 为什么不调用setException

Also I would like to know why the Thread.UncaughtExceptionHandler mechanism isn't used by FutureTask , is there any reason for this? 另外我想知道为什么FutureTask不使用Thread.UncaughtExceptionHandler机制,有什么理由吗?

setException probably isn't made for overriding, but is provided to let you set the result to an exception, should the need arise. setException可能不是用于覆盖,而是用于在需要时将结果设置为异常。 What you want to do is override the done() method and try to get the result: 你想要做的是覆盖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);
        }
    }
}

Have you tried using an UncaughtExceptionHandler ? 您是否尝试过使用UncaughtExceptionHandler

  • You need to implement the UncaughtExceptionHandler interface. 您需要实现UncaughtExceptionHandler接口。
  • To set an UncaughtExceptionHandler for pool threads, provide a ThreadFactory in the Executor.newCachedThreadPool(ThreadFactory) call. 要为池线程设置UncaughtExceptionHandler ,请在Executor.newCachedThreadPool(ThreadFactory)调用中提供ThreadFactory
  • You can set the UncaughtExceptionHandler for the created thread via setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) 您可以通过setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)为创建的线程设置UncaughtExceptionHandler

Submit the tasks with ExecutorService.execute , because only exceptions thrown from tasks submitted with execute make it to the uncaught exception handler. 使用ExecutorService.execute提交任务,因为只有从使用execute提交的任务抛出的异常才能使其成为未捕获的异常处理程序。 For Tasks submitted with ExecutorService.submit any thrown exception is considered to be part of the task's return value. 对于使用ExecutorService.submit提交的任务,任何抛出的异常都被视为任务返回值的一部分。 If a task submitted with submit terminates with an exception, it is rethrown when calling Future.get , wrapped in an ExecutionException 如果使用submit提交的任务以异常终止,则在调用Future.get时会Future.get ,包含在ExecutionException

A much better solution: Java FutureTask completion check 一个更好的解决方案: Java FutureTask完成检​​查

When you call futureTask.get() to retrieve the result of the computation it will throw an exception (ExecutionException) if the underlying Runnable / Callable threw an exception. 当调用futureTask.get()来检索计算结果时,如果底层Runnable / Callable引发异常,它将抛出异常(ExecutionException)。

ExecutionException.getCause() will return the exception that the Runnable / Callable threw. ExecutionException.getCause()将返回Runnable / Callable抛出的异常。

It will also throw a different exception if the Runnable / Callable was canceled. 如果Runnable / Callable被取消,它也会抛出一个不同的异常。

I have looked at the source code of FutureTask and could not find where setException is being called. 我查看了FutureTask的源代码,但无法找到调用setException位置。
There is an innerSetException method from FutureTask.Sync (inner class of FutureTask ) that is being called in case of an Throwable being thrown by the run method. 有一个innerSetException从方法FutureTask.Sync (内部类的FutureTask ),其被称为在的情况下Throwable由run方法抛出。 This method is also being called in setException . 此方法也在setExceptionsetException
So it seams like the javadoc is not correct (or very hard to understand...). 所以像javadoc这样的接缝不正确(或者很难理解......)。

There are three standard ways and one improvised way. 有三种标准方式和一种即兴方式。 1. use UncaughtExceptionHandler, set the UncaughtExceptionHandler for the created thread as 1.使用UncaughtExceptionHandler,将创建的线程的UncaughtExceptionHandler设置为

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

*But the limitation is it catches the exception thrown by thread but in case of future task, it is swallowed. *但是限制是它捕获线程抛出的异常,但是在未来任务的情况下,它被吞噬。 2. use afterExecute after making a custom threadpoolexecutor with hook that has been provided specially for this purpose. 2.在使用专门为此目的提供的钩子制作自定义threadpoolexecutor之后使用afterExecute Looking through the code of ThreadpoolExecutor, via submit > execute (there is a workQueue, workQueue.offer ), the tasks are added to the work queue 查看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. Then, the third is using simple try catch inside the call method but you can not catch the exception outside here. 然后,第三个是在调用方法中使用简单的try catch但是你不能在这里捕获异常。

  2. The workaround is calling all the call methods from a call method of a TaskFactory, a factory that releases callables. 解决方法是从TaskFactory的调用方法调用所有调用方法,TaskFactory是一个释放callables的工厂。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM