CompletableFuture通过exceptionally、handle和whenComplete实现异常处理:1. exceptionally捕获异常并返回默认值,适用于可预期错误;2. handle统一处理成功与失败情况,灵活返回结果或转换异常;3. whenComplete用于日志记录或资源清理,不改变结果;4. 链式调用中异常会跳过后续步骤直至被处理,建议在链尾添加兜底逻辑。合理组合这些方法可提升异步程序的稳定性和容错能力。
在Java异步编程中,CompletableFuture 是实现非阻塞任务编排和高效并发处理的核心工具。但异步任务难免出现异常,如何结合异常处理机制保障程序的稳定性,是构建健壮系统的关键。下面介绍几种常用方式,帮助你在使用 CompletableFuture 时有效应对异常。
当某个异步任务抛出异常时,可以使用 exceptionally 方法捕获异常,并返回一个默认结果,避免整个链式调用中断。
示例:
CompletableFuturefuture = CompletableFuture .supplyAsync(() -> { if (Math.random() < 0.5) throw new RuntimeException("请求失败"); return "正常结果"; }) .exceptionally(ex -> { System.out.println("捕获异常: " + ex.getMessage()); return "备用结果"; }); System.out.println(future.join()); // 可能输出“备用结果”
handle 方法比 exceptionally 更灵活,它无论是否发生异常都会执行,接收两个参数:结果和异常。你可以根据情况决定返回值。
示例:
CompletableFuturefuture = CompletableFuture .supplyAsync(() -> { return callRemoteService(); // 可能抛出异常 }) .handle((result, ex) -> { if (ex != null) { System.err.println("调用失败: " + ex.getMessage()); return "服务不可用,请稍后重试"; } return "响应: " + result; });
whenComplete 用于执行副作用操作(如日志、资源释放),不改变返回结果,也不能阻止异常传播。
示例:
CompletableFuturefuture = CompletableFuture .supplyAsync(() -> fetchData()) .whenComplete((result, ex) -> { if (ex != null) { System.out.println("任务失败,原因: " + ex.getClass()); } else { System.out.println("任务成功,结果长度: " + result.length()); } });
在多个 thenCompose、thenApply 链式调用中,一旦某一步抛出异常,后续步骤将跳过,直到遇到 exceptionally 或 handle 才会被处理。
示例:组合多个服务调用并容错
CompletableFuture基本上就这些。合理使用 exceptionally、handle 和 whenComplete,能让异步流程更稳定,提升系统的容错能力。关键是在设计阶段预判可能的失败点,并设置合适的恢复策略。result = serviceCall1() .thenCompose(data -> serviceCall2(data)) .thenApply(response -> formatResponse(response)) .exceptionally(ex -> { if (ex instanceof TimeoutException) { return "服务超时,使用缓存"; } else if (ex instanceof IOException) { return "网络问题,加载本地数据"; } return "未知错误"; });