在 java8 中处理 CompletableFuture 中的运行时异常

Handling Runtime Exception in CompletableFuture in java8

下面是我用来理解 java8 中 completablefuture 异常处理的示例代码。 如果我们按照文档使用异常方法, exceptionally 方法甚至还捕获运行时异常并继续处理管道中的最后一个块。

如果我们不使用 exceptionally 方法,它只会打印 运行 并退出。

如果我的理解不正确,请纠正我。

问题是假设我想抛出运行时异常并希望应用程序停止。基本上,如果我抛出运行时异常,它不应该进入管道中的下一个块。我应该怎么做。任何指针都有帮助。

public static void main(String[] args) {
    final CompletableFuture<String> retrieveName = CompletableFuture.supplyAsync(() -> {
        System.out.println("running");
        int i = 0;
        if(i == 0) {
            throw new RuntimeException("ding");
        }
        return "test";
    }).exceptionally(it -> {
        System.out.println(it.getMessage());
        return "empty";
    }).thenApply(it -> {

        System.out.println("last block" + it);
        return "dummy";
    });
}

试试这个:

public static void main(String[] args) {
    try {
        final CompletableFuture<String> retrieveName = CompletableFuture.supplyAsync(() -> {
            System.out.println("running");
            int i = 0;
            if (i == 0) {
                throw new RuntimeException("ding");
            }
            return "test";
        }).exceptionally(it -> {
            if (it.getMessage().contains("ding")) {
                throw (RuntimeException) it;
            }
            System.out.println(it.getMessage());
            return "empty";
        }).thenApply(it -> {
            System.out.println("last block" + it);
            return "dummy";
        });
        retrieveName.join();
    } catch (Exception e) {
        System.out.println("main() exception, cause=" + e.getCause());
    }
}

这是输出:

running

main() exception, cause=java.lang.RuntimeException: ding

我对您的代码做了 3 处小改动:

  • 在 try-catch 中将其全部包裹起来
  • exceptionally() 中为 "ding" 异常抛出 RuntimeException
  • 添加了对 retrieveName.join() 的调用。来自 CompletableFuture.join():
  • 的 Javadoc

public T join​()

Returns the result value when complete, or throws an (unchecked) exception if completed exceptionally.


根据OP反馈更新------>

Lets say if i want to throw runtime exception and want application to stop. Basically if i throw Runtime exception , it shouldn't proceed to next block in pipeline. How should i do that.

您只需更改 2 处代码即可实现您想要的效果:

[1] 完全删除 exceptionally() 回调,以便 CompletableFuture (CF) 以异常终止。在 OP 代码的 exceptionally() 中,异常被吞没而不是重新抛出,并且 returning 一个 CF,所以 thenApply() 方法仍然执行。

[2] 在 main() 的末尾添加对 retrieveName.join() 的调用。这是一个阻塞调用,但由于线程已终止并出现与示例代码无关的异常。 join() 方法将提取抛出的 RunTimeException 并重新抛出,包裹在 CompletionException 中.

这是您修改后的代码:

 public static void main(String[] args) {
    final CompletableFuture<String> retrieveName = CompletableFuture.supplyAsync(() -> {
        System.out.println("running");
        int i = 0;
        if(i == 0) {
            throw new RuntimeException("ding");
        }
        return "test";
    }).thenApply(it -> {
        System.out.println("last block" + it);
        return "dummy";
    });
    retrieveName.join();
}

备注:

  • 这不是生产环境中的做法。来自 join() 的阻塞调用在这里不是问题,但可能会持续很长时间 运行 CF。但是您显然无法在 CF 完成之前从 CF 中提取异常,因此 join() 调用阻塞是有道理的。
  • 始终牢记 main() 与 CF 运行 不在同一线程中。
  • 另一种方法(如果可行)可能是在 exceptionally() 中处理所有必要的 post-异常操作(日志记录等),然后终止通常使用合适的 return 值(例如 "Exception handled!")而不是传播异常。
  • 您可以通过调用非阻塞isDone()方法来检查CF是否仍然是运行。您还可以检查 CF 是否以异常结束 (isCompletedExceptionally()) 或被取消 (isCancelled ()).