处理来自 Java ExecutorService 任务的异常

新手上路,请多包涵

我正在尝试使用 Java 的 ThreadPoolExecutor 类来运行具有固定线程数的大量重量级任务。每个任务都有很多地方可能由于异常而失败。

我已经继承了 ThreadPoolExecutor 并且我已经覆盖了 afterExecute 方法,该方法应该提供运行任务时遇到的任何未捕获的异常。但是,我似乎无法让它工作。

例如:

 public class ThreadPoolErrors extends ThreadPoolExecutor {
    public ThreadPoolErrors() {
        super(  1, // core threads
                1, // max threads
                1, // timeout
                TimeUnit.MINUTES, // timeout units
                new LinkedBlockingQueue<Runnable>() // work queue
        );
    }

    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if(t != null) {
            System.out.println("Got an error: " + t);
        } else {
            System.out.println("Everything's fine--situation normal!");
        }
    }

    public static void main( String [] args) {
        ThreadPoolErrors threadPool = new ThreadPoolErrors();
        threadPool.submit(
                new Runnable() {
                    public void run() {
                        throw new RuntimeException("Ouch! Got an error.");
                    }
                }
        );
        threadPool.shutdown();
    }
}

该程序的输出是“一切正常——情况正常!”即使提交给线程池的唯一 Runnable 抛出异常。有什么线索吗?

谢谢!

原文由 Tom 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 783
2 个回答

文档

注意:当动作被显式地或通过诸如提交等方法包含在任务(例如FutureTask)中时,这些任务对象捕获并维护计算异常,因此它们不会导致突然终止,并且内部异常不会传递给该方法.

当你提交一个 Runnable 时,它会被包裹在一个 Future 中。

你的 afterExecute 应该是这样的:

 public final class ExtendedExecutor extends ThreadPoolExecutor {

    // ...

    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if (t == null && r instanceof Future<?>) {
            try {
                Future<?> future = (Future<?>) r;
                if (future.isDone()) {
                    future.get();
                }
            } catch (CancellationException ce) {
                t = ce;
            } catch (ExecutionException ee) {
                t = ee.getCause();
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
        if (t != null) {
            System.out.println(t);
        }
    }
}

原文由 nos 发布,翻译遵循 CC BY-SA 4.0 许可协议

警告:需要注意的是,这个解决方案会阻塞 future.get() 中的调用线程。


如果要处理任务抛出的异常,那么通常最好使用 Callable 而不是 Runnable

Callable.call() 允许抛出已检查的异常,这些异常会传播回调用线程:

 Callable task = ...
Future future = executor.submit(task);
// do something else in the meantime, and then...
try {
   future.get();
} catch (ExecutionException ex) {
   ex.getCause().printStackTrace();
}

如果 Callable.call() 抛出异常,这将被包裹在 ExecutionException 并由 Future.get() 抛出。

这可能比子类 ThreadPoolExecutor 更可取。如果异常是可恢复的,它还使您有机会重新提交任务。

原文由 skaffman 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题