MST

星途 面试题库

面试题:Java中CompletableFuture获取返回值与异常信息的复杂场景处理

假设存在多个CompletableFuture任务,它们之间存在依赖关系,如任务A完成后触发任务B和任务C并行执行,任务B和C完成后触发任务D。在这个过程中,如果某个任务出现异常,要如何统一捕获并处理异常,同时还要确保正常任务的返回值能正确传递到后续任务中,给出详细的代码实现和设计思路。
31.5万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 任务依赖构建:使用CompletableFuture的方法来构建任务之间的依赖关系。例如,thenCompose用于顺序依赖,thenCombine用于并行任务完成后的合并。
  2. 异常处理:通过exceptionally方法来捕获任务执行过程中的异常,并返回一个默认值或者进行异常处理。在捕获异常后,我们可以将异常信息封装在返回值中,以便后续任务能够知晓异常情况。
  3. 返回值传递:确保正常任务的返回值能够正确传递到后续依赖任务中。通过thenApply等方法对任务结果进行处理,并传递给下一个任务。

代码实现

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

public class CompletableFutureDependencyExample {

    public static void main(String[] args) {
        CompletableFuture<String> taskA = CompletableFuture.supplyAsync(() -> {
            // 模拟任务A执行
            System.out.println("Task A is running");
            return "Result of Task A";
        });

        CompletableFuture<String> taskB = taskA.thenApplyAsync(resultA -> {
            // 模拟任务B执行,依赖任务A的结果
            System.out.println("Task B is running with result from A: " + resultA);
            // 这里可以抛出异常模拟任务失败
            if (Math.random() < 0.5) {
                throw new RuntimeException("Task B failed");
            }
            return "Result of Task B";
        });

        CompletableFuture<String> taskC = taskA.thenApplyAsync(resultA -> {
            // 模拟任务C执行,依赖任务A的结果
            System.out.println("Task C is running with result from A: " + resultA);
            // 这里可以抛出异常模拟任务失败
            if (Math.random() < 0.5) {
                throw new RuntimeException("Task C failed");
            }
            return "Result of Task C";
        });

        CompletableFuture<String> taskD = CompletableFuture.allOf(taskB, taskC)
               .thenApplyAsync(v -> {
                    try {
                        // 获取任务B和任务C的结果
                        String resultB = taskB.get();
                        String resultC = taskC.get();
                        System.out.println("Task D is running with results from B and C: " + resultB + ", " + resultC);
                        return "Result of Task D";
                    } catch (InterruptedException | ExecutionException e) {
                        // 捕获任务B或任务C执行过程中的异常
                        throw new RuntimeException("Task B or C failed", e);
                    }
                });

        taskD.exceptionally(ex -> {
            // 统一捕获任务D执行过程中的异常,包括任务B和任务C传递过来的异常
            System.out.println("An exception occurred: " + ex.getMessage());
            return "Default result due to exception";
        }).thenAccept(System.out::println);
    }
}

在上述代码中:

  1. 任务A:是一个独立的任务,通过supplyAsync异步执行并返回结果。
  2. 任务B和任务C:依赖任务A的结果,通过thenApplyAsync异步执行,并可以在执行过程中抛出异常。
  3. 任务D:依赖任务B和任务C的完成,通过allOf方法等待任务B和任务C都完成后执行。在任务D中,通过get方法获取任务B和任务C的结果,并处理可能的异常。
  4. 异常处理:通过exceptionally方法统一捕获任务执行过程中的异常,并返回默认结果。这样可以确保即使某个任务出现异常,整个流程依然可以继续执行并得到处理结果。