代码实现
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExceptionHandling {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
if (Math.random() < 0.5) {
throw new RuntimeException("第一个任务抛出异常");
}
return "第一个任务结果";
})
.thenCompose(result -> {
System.out.println("第二个任务开始执行,结果: " + result);
return CompletableFuture.completedFuture("第二个任务结果");
})
.exceptionally(ex -> {
System.out.println("捕获到异常: " + ex.getMessage());
return null;
})
.thenAccept(System.out::println);
}
}
不同处理方式及优缺点
- 使用
exceptionally
方法:
- 优点:简单直接,在链式调用中可以方便地捕获前序任务抛出的异常,并返回一个默认值或者进行一些异常处理操作。例如在上述代码中,
exceptionally
捕获到第一个任务抛出的异常并打印异常信息,同时返回null
,避免第二个任务执行。
- 缺点:如果异常处理逻辑较为复杂,代码可读性可能会受影响,尤其是在多个
CompletableFuture
链式调用时,异常处理代码会与正常业务逻辑混合在一起。
- 使用
handle
方法:
- 优点:
handle
方法既可以处理正常结果,也可以处理异常情况,统一在一个方法内进行逻辑处理,使代码结构相对紧凑。
- 缺点:对于只想单纯处理异常而不涉及正常结果处理的场景,
handle
方法可能会显得过于冗余,因为必须同时处理正常和异常情况。示例代码如下:
CompletableFuture.supplyAsync(() -> {
if (Math.random() < 0.5) {
throw new RuntimeException("第一个任务抛出异常");
}
return "第一个任务结果";
})
.handle((result, ex) -> {
if (ex != null) {
System.out.println("捕获到异常: " + ex.getMessage());
return null;
}
System.out.println("第二个任务开始执行,结果: " + result);
return "第二个任务结果";
})
.thenAccept(System.out::println);
- 使用
whenComplete
方法(结合isCompletedExceptionally
方法):
- 优点:
whenComplete
方法在任务完成(正常完成或异常完成)时都会执行,可以在其中根据isCompletedExceptionally
判断任务是否异常完成,进行不同的处理逻辑,灵活性较高。
- 缺点:
whenComplete
方法不返回新的CompletableFuture
,不能直接在其内部处理异常并返回处理结果,需要额外的变量或者其他方式来传递处理结果,增加了代码的复杂性。示例代码如下:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
if (Math.random() < 0.5) {
throw new RuntimeException("第一个任务抛出异常");
}
return "第一个任务结果";
});
future.whenComplete((result, ex) -> {
if (ex != null) {
System.out.println("捕获到异常: " + ex.getMessage());
} else {
System.out.println("第二个任务开始执行,结果: " + result);
// 这里需要另起一个CompletableFuture来模拟第二个任务
CompletableFuture.supplyAsync(() -> "第二个任务结果")
.thenAccept(System.out::println);
}
});