如何处理来自CompletableFuture.runAsync的未捕获异常

时间:2018-11-09 00:56:34

标签: java multithreading asynchronous threadpool java.util.concurrent

我们的应用程序具有一些异步运行的代码,但失败了。像这样:

CompletableFuture.runAsync(
    () -> { throw new RuntimeException("bad"); },
    executorService
);

我们希望默认的异常处理代码能够捕获这些错误,以防万一特定用途忘记处理异常(这是由生产错误引起的)。

这显然很棘手。 Handling exceptions from Java ExecutorService tasks中给出的答案无效。

它依赖于任务为Future<?>,然后在其上调用get(),从而导致再次引发异常。但这不是runAsync代码的情况。

runAsync创建一个java.util.concurrent.CompletableFuture.AsyncRun类,该类似乎试图抑制所有异常。尽管本身是Future,但 not 并不表示自己是isDone(),并且似乎没有办法摆脱异常。

因此,考虑到以下样板,我们应该如何捕获这些过时的异常?

请注意,我们确实希望某些东西能够捕获runAsync代码中的所有未处理的异常,而不是我们可以为每个runAsync调用添加的东西。忘记将处理代码添加到每个代码中太容易了。

public class ExceptionTest {
    public static void main(String[] args) throws RuntimeException {
        ExecutorService executorService = new ThreadPoolExecutor(
            1, 1, 0L,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue()
        ) {
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);

                // TODO: Magically extract the exception from `r`
            }
        };

        CompletableFuture.runAsync(
            () -> { throw new RuntimeException("bad"); },
            executorService
        );
    }
}

1 个答案:

答案 0 :(得分:0)

因此,这是一个可怕的骇客,但确实可以解决使用t1.join(); t2.join(); t3.join(); t4.join();时忘记调用exceptionally的情况。我希望看到更通用,更简单的解决方案。

它的工作原理是在执行之前拦截runAsync 并在AsyncRun块上进行修补。

但是,很严重。但这可能会起作用,直到Oracle更改exceptionally的工作方式。

runAsync