CompletableFuture:整合、超时、完成事件与批量处理

引言

在异步编程实践中,我们不仅需要处理单个任务的执行流程,更需要应对多个异步任务之间的复杂交互。本文将通过实际案例解析以下核心功能:

  • 双任务整合:合并两个独立任务的结果
  • 高效超时控制:防止异步操作无限等待
  • 完成事件处理:实时响应任务完成状态
  • 批量任务管理 :使用 allOf 处理并行任务组

一、双任务结果整合

1.1 thenCombine 方法

合并两个独立任务的执行结果,适用于需要同时获取两个异步结果的场景

java 复制代码
CompletableFuture<String> userInfo = getUserInfoAsync(userId);
CompletableFuture<Double> balance = getAccountBalanceAsync(accountId);

userInfo.thenCombine(balance, (info, money) -> {
    return String.format("用户 %s 余额 %.2f", info, money);
}).thenAccept(System.out::println);

执行流程

  1. 并行获取用户信息和账户余额
  2. 当两个任务都完成时执行合并操作
  3. 输出格式化字符串

1.2 thenCompose 方法

串联两个依赖型任务,前一个任务的结果作为下一个任务的输入

java 复制代码
CompletableFuture<Order> orderFuture = fetchOrderAsync("O123");

orderFuture.thenCompose(order -> 
    calculateTaxAsync(order.getAmount())
).thenAccept(tax -> 
    System.out.println("应缴税款:" + tax)
);

二、高效超时控制

2.1 Java 9+ 原生超时支持

java 复制代码
CompletableFuture<String> dataFuture = fetchDataFromNetwork()
    .orTimeout(3, TimeUnit.SECONDS)
    .exceptionally(ex -> {
        if (ex instanceof TimeoutException) {
            return "默认数据";
        }
        throw new CompletionException(ex);
    });

2.2 Java 8 兼容方案

java 复制代码
ExecutorService timeoutExecutor = Executors.newScheduledThreadPool(2);

<T> CompletableFuture<T> withTimeout(CompletableFuture<T> future, 
                                    long timeout, TimeUnit unit) {
    CompletableFuture<T> timeoutFuture = new CompletableFuture<>();
    timeoutExecutor.schedule(() -> {
        if (!future.isDone()) {
            timeoutFuture.completeExceptionally(new TimeoutException());
        }
    }, timeout, unit);
    return future.applyToEither(timeoutFuture, Function.identity());
}

// 使用示例
withTimeout(fetchData(), 2, TimeUnit.SECONDS)
    .exceptionally(ex -> handleTimeout(ex));

三、完成事件处理

3.1 whenComplete 方法

无论成功或异常都会触发的完成回调

java 复制代码
processImageAsync(imageFile)
    .whenComplete((result, ex) -> {
        if (ex != null) {
            log.error("图片处理失败", ex);
            sendAlert(ex.getMessage());
        } else {
            updateUI(result);
        }
    });

3.2 thenAccept 方法

仅处理成功结果的消费者

java 复制代码
generateReportAsync()
    .thenAccept(report -> {
        saveToDatabase(report);
        sendEmailNotification(report);
    })
    .exceptionally(ex -> {
        log.error("报告生成失败", ex);
        return null;
    });

四、批量任务管理

4.1 allOf 方法

等待所有任务完成(不保留结果顺序)

java 复制代码
List<String> userIds = Arrays.asList("U1001", "U1002", "U1003");

List<CompletableFuture<UserProfile>> futures = userIds.stream()
    .map(id -> fetchUserProfileAsync(id))
    .collect(Collectors.toList());

CompletableFuture<Void> allFutures = CompletableFuture.allOf(
    futures.toArray(new CompletableFuture[0])
);

allFutures.thenRun(() -> {
    List<UserProfile> profiles = futures.stream()
        .map(CompletableFuture::join)
        .collect(Collectors.toList());
    analyzeProfiles(profiles);
});

4.2 anyOf 方法

任一任务完成即触发

java 复制代码
CompletableFuture<Object> anyResult = CompletableFuture.anyOf(
    queryFromCache(),
    queryFromDB(),
    queryFromRemoteService()
);

anyResult.thenAccept(result -> 
    System.out.println("最快返回结果: " + result)
);

五、综合实战案例

电商订单支付流程

java 复制代码
public class PaymentProcessor {

    // 核心支付方法
    public CompletableFuture<PaymentResult> processPayment(PaymentRequest request) {
        return validateRequest(request)
            .thenCompose(valid -> 
                reserveInventory(request.getItems())
            )
            .thenCombine(
                calculateTax(request.getAmount()), 
                (inventoryId, tax) -> buildPaymentPayload(request, inventoryId, tax)
            )
            .thenCompose(payload -> 
                callPaymentGateway(payload)
                    .orTimeout(10, TimeUnit.SECONDS)
            )
            .handle((result, ex) -> {
                if (ex != null) {
                    rollbackOperations();
                    return new PaymentResult(FAILURE, ex.getMessage());
                }
                return result;
            });
    }

    // 辅助方法省略...
}

流程说明

  1. 请求验证(同步)
  2. 库存预留(异步)
  3. 并行计算税费
  4. 组合支付请求
  5. 调用支付接口(带超时)
  6. 统一处理结果/异常

六、性能优化要点

  1. 线程池分层

    java 复制代码
    // CPU密集型任务
    ExecutorService cpuBoundPool = Executors.newWorkStealingPool();
    
    // IO密集型任务
    ExecutorService ioBoundPool = Executors.newCachedThreadPool();
    
    // 定时任务
    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
  2. 结果缓存策略

    java 复制代码
    private final Map<String, CompletableFuture<ProductInfo>> cache = 
        new ConcurrentHashMap<>();
    
    public CompletableFuture<ProductInfo> getProduct(String id) {
        return cache.computeIfAbsent(id, 
            k -> fetchFromRemote(id).thenApply(this::parseResponse));
    }

七、常见问题排查

7.1 回调链未触发

  • 检查是否遗漏异常处理
  • 确认线程池未饱和

7.2 内存泄漏

  • 及时清理已完成的Future引用
  • 使用WeakReference持有上下文

7.3 死锁问题

  • 避免在回调中同步等待其他Future
  • 使用非阻塞的组合方法

结语

通过合理运用 CompletableFuture 的组合功能,我们可以构建出:

  • 支持超时熔断的健壮系统
  • 高效并行的批量处理流程
  • 实时响应的事件驱动架构
相关推荐
狐凄18 分钟前
Python实例题:使用Pvthon3编写系列实用脚本
java·网络·python
Lxinccode2 小时前
Java查询数据库表信息导出Word-获取数据库实现[1]:KingbaseES
java·数据库·word·获取数据库信息·获取kingbasees信息
元亓亓亓3 小时前
Java后端开发day36--源码解析:HashMap
java·开发语言·数据结构
sd21315123 小时前
RabbitMQ 复习总结
java·rabbitmq
码银5 小时前
Java 集合:泛型、Set 集合及其实现类详解
java·开发语言
东阳马生架构5 小时前
Nacos简介—4.Nacos架构和原理
java
一只叫煤球的猫6 小时前
你真的会用 return 吗?—— 11个值得借鉴的 return 写法
java·后端·代码规范
颇有几分姿色7 小时前
Spring Boot 读取配置文件的几种方式
java·spring boot·后端
爱编程的鱼7 小时前
C# 枚举(Enum)声明与使用详解
java·windows·c#
人生导师yxc7 小时前
Spring MVC
java·spring·mvc