MST

星途 面试题库

面试题:Java中CompletableFuture复杂场景下的错误恢复与资源管理

在一个涉及多线程异步任务、数据库操作以及外部服务调用的复杂场景中,使用CompletableFuture进行任务编排。当异步任务执行过程中出现错误时,不仅要实现错误恢复,还要保证数据库事务的一致性以及正确释放外部服务资源。请详细阐述你的设计思路,并给出关键代码片段,说明如何在不同的错误类型下进行针对性的处理。
45.1万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 任务编排:使用 CompletableFuture 来编排多线程异步任务,利用其链式调用和组合方法,如 thenApplythenCompose 等,按顺序执行任务。
  2. 错误处理:通过 exceptionally 方法捕获异步任务中的异常,并进行错误恢复处理。根据不同的异常类型进行针对性处理。
  3. 数据库事务一致性:在数据库操作时,利用数据库事务管理机制,如 Spring 的 @Transactional 注解(假设使用 Spring 框架)。如果异步任务中数据库操作失败,回滚事务以保证一致性。
  4. 外部服务资源释放:为外部服务调用编写资源释放逻辑,确保在出现异常时能正确释放资源。可以使用 try - finally 块或者 Java 7 引入的 try - with - resources 语法。

关键代码片段

假设使用 Java 8 和 Spring 框架,以下是示例代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
public class TaskService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private ExternalService externalService;

    @Transactional
    public CompletableFuture<String> executeTasks() {
        return CompletableFuture.supplyAsync(() -> {
            // 模拟异步任务 1
            if (Math.random() > 0.5) {
                throw new RuntimeException("Async Task 1 failed");
            }
            return "Task 1 completed";
        })
      .thenApply(result1 -> {
            // 模拟异步任务 2,依赖任务 1 的结果
            if (Math.random() > 0.5) {
                throw new RuntimeException("Async Task 2 failed");
            }
            return result1 + ", Task 2 completed";
        })
      .thenCompose(result2 -> {
            // 调用外部服务
            return CompletableFuture.supplyAsync(() -> {
                try {
                    return externalService.callExternalService(result2);
                } catch (Exception e) {
                    throw new RuntimeException("External service call failed", e);
                }
            });
        })
      .thenApply(result3 -> {
            // 数据库操作
            jdbcTemplate.update("INSERT INTO tasks (result) VALUES (?)", result3);
            return result3 + ", Database operation completed";
        })
      .exceptionally(ex -> {
            // 错误恢复处理
            if (ex instanceof RuntimeException) {
                if (ex.getMessage().contains("Async Task 1 failed")) {
                    // 针对任务 1 失败的恢复逻辑
                    System.out.println("Recovering from Async Task 1 failure");
                } else if (ex.getMessage().contains("Async Task 2 failed")) {
                    // 针对任务 2 失败的恢复逻辑
                    System.out.println("Recovering from Async Task 2 failure");
                } else if (ex.getMessage().contains("External service call failed")) {
                    // 针对外部服务调用失败的恢复逻辑
                    externalService.releaseResources();
                    System.out.println("Recovering from external service call failure");
                } else {
                    // 通用恢复逻辑
                    System.out.println("General recovery from failure");
                }
            }
            return "Task failed: " + ex.getMessage();
        });
    }
}

class ExternalService {
    public String callExternalService(String input) throws Exception {
        // 模拟外部服务调用
        if (Math.random() > 0.5) {
            throw new Exception("External service error");
        }
        return input + ", External service call completed";
    }

    public void releaseResources() {
        // 释放外部服务资源逻辑
        System.out.println("External service resources released");
    }
}

不同错误类型下的针对性处理

  1. 异步任务自身错误:在 exceptionally 块中,通过判断异常信息中是否包含特定任务失败的标识,进行针对性恢复处理。如 if (ex.getMessage().contains("Async Task 1 failed"))
  2. 外部服务调用错误:捕获外部服务调用时抛出的异常,在 exceptionally 块中调用 externalService.releaseResources() 释放资源,并进行相应恢复逻辑。
  3. 数据库操作错误:由于使用了 @Transactional 注解,数据库操作失败时会自动回滚事务,保证一致性。exceptionally 块中可以记录错误日志等通用恢复操作。