MST

星途 面试题库

面试题:Java异步流中如何优雅地处理CompletableFuture链中的错误

在Java的异步编程中,我们经常会使用`CompletableFuture`构建异步操作链。假设你有一个`CompletableFuture`链,其中每个`CompletableFuture`都可能抛出不同类型的异常。请阐述如何优雅地处理这些异常,使得整个异步流在出现错误时能够进行适当的恢复或者记录日志,并给出相应的代码实现。
45.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试
  1. 异常处理方式阐述

    • 使用exceptionally方法:CompletableFuture提供了exceptionally方法,它可以在CompletableFuture链中的任何一个环节出现异常时捕获并处理异常。该方法接收一个Function作为参数,这个Function以抛出的异常为输入,返回一个默认值或者经过处理的值,用于替代出现异常的CompletableFuture原本应该返回的结果,从而使得异步流能够继续执行下去。
    • 使用whenCompletehandle方法:whenComplete方法可以在CompletableFuture完成(无论是正常完成还是异常完成)时执行一段代码,它可以用来记录日志等操作。handle方法类似于whenComplete,但它可以返回一个新的值,用于替代出现异常时原本应该返回的结果,从而实现恢复逻辑。
  2. 代码实现

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureExceptionHandling {
    public static void main(String[] args) {
        CompletableFuture.supplyAsync(() -> {
            if (Math.random() > 0.5) {
                throw new RuntimeException("模拟异常");
            }
            return "任务1成功";
        })
       .thenApply(result -> {
            System.out.println("任务1结果: " + result);
            return "任务2输入: " + result;
        })
       .thenApplyAsync(result -> {
            if (Math.random() > 0.5) {
                throw new IllegalArgumentException("另一个模拟异常");
            }
            return "任务2成功: " + result;
        })
       .exceptionally(ex -> {
            System.out.println("捕获到异常: " + ex.getMessage());
            return "异常恢复值";
        })
       .whenComplete((result, ex) -> {
            if (ex != null) {
                System.out.println("日志记录 - 异常: " + ex.getMessage());
            } else {
                System.out.println("日志记录 - 正常结果: " + result);
            }
        })
       .thenAccept(System.out::println);

        try {
            // 等待所有异步操作完成
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中:

  • supplyAsync启动一个异步任务,模拟可能抛出异常的操作。
  • thenApplythenApplyAsync构建异步操作链,同样模拟了可能抛出异常的操作。
  • exceptionally捕获前面操作抛出的异常,并返回一个恢复值,使得异步流可以继续执行。
  • whenComplete用于记录日志,根据是否有异常打印不同的信息。
  • thenAccept用于消费最终的结果(包括异常恢复后的结果)并打印。Thread.sleep用于等待所有异步操作完成。