completablefuture的使用

本文详细介绍了JavaCompletableFuture在项目中如何解决接口调用链路中的同步问题,通过异步模型提高性能,包括Future和CompletionStage的概念、使用方法以及在API异步化中的应用,展示了异步化后TP99响应时间的显著提升。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CompletableFuture使用详解

【Java异常】Variable used in lambda expression should be final or effectively final

CompletableFuture原理与实践-外卖商家端API的异步化
CompletableFuture异步编排

项目描述

项目接口需要从下游多个接口获取数据,并且下游的网络不稳定还会涉及到循环调用下游接口,导致该接口响应异常慢。

其实调用链路如图所示

在这里插入图片描述

cf1 ~ cf5 都需要调用下游接口,导致只能按照顺序,依次调用接口。其实可以将,例如cf1,cf2 拆分成两个异步请求,没必要cf2必须等待cf1请求完成才请求。

CompletableFuture 简介

之前使用的是同步模型。在同步调用的场景下,接口耗时长、性能差,接口响应时长T > T1+T2+T3+……+Tn。

利用 CompletableFuture 之后,使用的是异步模型,并行从下游获取数据。

Completable实现了两个接口:FutureCompletionStage

  • Future:表示异步计算结果
  • CompletionStage:表示异步执行过程中的一个步骤,这个步骤可能是由另外一个 CompletionStage触发的。

随着当前步骤的完成,也可能会触发其他一系列CompletionStage的执行。从而我们可以根据实际业务对这些步骤进行多样化的编排组合,CompletionStage接口正是定义了这样的能力,我们可以通过其提供的thenAppy、thenCompose等函数式编程方法来组合编排这些步骤。

CompletableFuture 内部使用了 ForkJoinPool 来执行异步任务。ForkJoinPool 是一个工作窃取的线程池,它能够有效利用多核处理器的计算能力。

get() 方法

get() 方法是 Future 接口中的一个方法,它阻塞当前线程直到 Future 完成。

如果 Future 正常完成,get() 方法将返回计算结果;

如果 Future 被取消,它将抛出 CancellationException;

如果 Future 抛出异常,它将抛出 ExecutionException。

join() 方法

join() 方法是 CompletableFuture 特有的方法,它阻塞当前线程直到 CompletableFuture 完成。

如果 CompletableFuture 正常完成,join() 方法将返回计算结果;

如果 CompletableFuture 被取消或抛出异常,它将抛出 CompletionException,这是一个未检查异常,该异常包装了原始的异常

区别

  • 异常处理:get()方法抛出的是ExecutionException,而join()方法抛出的是CompletionException。CompletionException是ExecutionException的子类,它提供了更多的信息,例如原始异常的类型和消息。
  • 中断处理:get()方法在被中断时会抛出InterruptedException,而join()方法不会。如果你需要处理中断,应该使用get()方法。
  • 使用场景:join()方法通常用于CompletableFuture链式调用中,因为它抛出的CompletionException可以被链式调用中的exceptionally方法捕获和处理。而get()方法通常用于需要捕获中断异常的场景。

使用教程

具体使用教程可以参考这边文章CompletableFuture原理与实践-外卖商家端API的异步化,描述的十分详细和生动。

项目应用

cf1,cf2由于没有任何依赖,可是需要返回值,故设计为

CompletableFuture<List<xxxDto>> cf1 = CompletableFuture.supplyAsync(() -> {
    //业务处理
    if (CollectionUtils.isEmpty(resp.getData())) {
        //其他线程抛出异常
    }
    return resp.getData();
}).exceptionally(ex -> {
    //主线程处理其他线程抛出的异常
});

cf3,cf5由于依赖 cf1, cf2 ,所以可以通过thenApply、thenAccept、thenCompose等方法来实现

但注意,返回数据类型必须相同。

CompletableFuture<xxxDto> cf3 = cf1.thenApply(result1 -> {
  //result1为cf1的结果
  //......
  return resp.getData();
});
CompletableFuture<xxxDto> cf5 = cf2.thenApply(result2 -> {
  //result2为cf2的结果
  //......
  return resp.getData();
});

cf4,依赖cf1,cf2 ,这种二元依赖可以通过thenCombine等回调来实现

CompletableFuture<String> cf4 = cf1.thenCombine(cf2, (result1, result2) -> {
  //result1和result2分别为cf1和cf2的结果
  return "result4";
});

由于cf6依赖cf3,cf4,cf5,这种多元依赖可以通过allOfanyOf方法来实现,区别是当需要多个依赖全部完成时使用allOf,当多个依赖中的任意一个完成即可时使用anyOf

CompletableFuture<Void> cf6 = CompletableFuture.allOf(cf3, cf4, cf5);
CompletableFuture<String> result = cf6.thenApply(v -> {
  //这里的join并不会阻塞,因为传给thenApply的函数是在CF3、CF4、CF5全部完成时,才会执行 。
  result3 = cf3.join();
  result4 = cf4.join();
  result5 = cf5.join();
  //根据result3、result4、result5组装最终result;
  return "result";
});

需注意,如果cfx里用到方法的局部变量需要设置为final,避免completablefuture在使用该变量的时候被修改。

设计思想

按照类似 “观察者模式” 的设计思想

异步化收益

接口响应从 TP99=3s左右,提升到1.3s左右

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值