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左右

### 关于 CompletableFuture使用方法 #### 1. 基本概念 `CompletableFuture` 是 Java 中用于异步编程的一个强大工具类。相比传统的 `Future`,它不仅支持获取执行结果的功能,还扩展了许多特性,例如异常处理、手动设置结果、链式操作以及结果组合等[^1]。 --- #### 2. 示例代码展示 以下是几个典型的 `CompletableFuture` 使用场景及其对应的代码示例: ##### (1)基本异步任务与结果处理 下面的代码演示了如何创建一个异步任务,并对其结果进行进一步处理: ```java import java.util.concurrent.CompletableFuture; public class BasicExample { public static void main(String[] args) throws Exception { // 定义异步任务 CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { System.out.println("Executing async task..."); return 42; }); // 对结果进行转换 CompletableFuture<String> result = future.thenApply(number -> "The answer is: " + number); // 获取最终结果 String finalResult = result.get(); System.out.println(finalResult); } } ``` 此代码片段展示了如何通过 `supplyAsync()` 方法启动一个异步任务,并利用 `thenApply()` 进行后续的结果处理[^2]。 --- ##### (2)并行运行多个任务 (`allOf`) 当需要同时运行多个异步任务时,可以使用 `allOf()` 方法。该方法会在所有指定的任务完成后才继续向下执行: ```java import java.util.concurrent.CompletableFuture; import java.util.Date; public class AllOfExample { public static void main(String[] args) throws Exception { // 定义两个异步任务 CompletableFuture<Void> task1 = CompletableFuture.runAsync(() -> { try { Thread.sleep(2000); System.out.println("Task 1 completed"); } catch (InterruptedException e) { e.printStackTrace(); } }); CompletableFuture<Void> task2 = CompletableFuture.runAsync(() -> { try { Thread.sleep(3000); System.out.println("Task 2 completed"); } catch (InterruptedException e) { e.printStackTrace(); } }); // 并行运行多个任务 CompletableFuture<Void> allTasks = CompletableFuture.allOf(task1, task2); allTasks.join(); System.out.println("All tasks finished at: " + new Date()); } } ``` 在此例子中,`allOf()` 确保只有在 `task1` 和 `task2` 都完成的情况下才会打印最后的消息[^3]。 --- ##### (3)任意一个任务完成即返回 (`anyOf`) 如果只需要其中一个任务的结果而不需要等待全部完成,则可采用 `anyOf()` 方法: ```java import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class AnyOfExample { public static void main(String[] args) throws ExecutionException, InterruptedException { // 定义两个异步任务 CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000); return "Result from Task 1"; } catch (InterruptedException e) { throw new RuntimeException(e); } }); CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(2000); return "Result from Task 2"; } catch (InterruptedException e) { throw new RuntimeException(e); } }); // 只要有一个任务完成就立即返回其结果 CompletableFuture<Object> anyTask = CompletableFuture.anyOf(task1, task2); System.out.println(anyTask.get()); // 主线程稍作延时以便观察效果 Thread.sleep(3000); } } ``` 这里展示了 `anyOf()` 如何快速响应最先完成的任务[^3]。 --- ##### (4)带超时机制的任务完成检测 为了防止某些长时间未完成的任务阻塞整个流程,可以通过 `get(long timeout, TimeUnit unit)` 设置超时时间: ```java import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; public class TimeoutExample { public static void main(String[] args) { CompletableFuture<Void> longRunningTask = CompletableFuture.runAsync(() -> { try { Thread.sleep(5000); // 模拟耗时任务 } catch (InterruptedException e) { e.printStackTrace(); } }); try { // 超过3秒则抛出TimeoutException longRunningTask.get(3, TimeUnit.SECONDS); System.out.println("Task completed within the time limit."); } catch (Exception e) { System.err.println("Task timed out or encountered an error: " + e.getMessage()); } } } ``` 这段代码说明了如何优雅地处理可能存在的长期挂起问题[^3]。 --- ### 总结 以上介绍了 `CompletableFuture` 的几种常见用法,包括但不限于基础异步任务管理、多任务并发控制(`allOf`)、最快完成者优先策略(`anyOf`)以及带有超时保护的操作模式。这些功能极大地增强了 Java异步编程领域的能力。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值