在 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 ()).
下面是我用来理解 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 ()).