【Java异步编程】CompletableFuture实现:异步任务的合并执行

devtools/2025/2/3 2:54:43/

文章目录

    • 一. 合并两个异步任务的结果
      • 1. `thenCombine()`:组合两个异步任务的结果
      • 2. `runAfterBoth()`:在两个任务完成后执行无返回值操作
      • 3. `thenAcceptBoth()`:消费两个任务的结果
    • 二. `allOf()`:等待所有任务完成

如果某个任务同时依赖另外两个异步任务的执行结果,就需要对另外两个异步任务进行合并。以泡茶喝为例,​“泡茶喝”任务需要对“烧水”任务与“清洗”任务进行合并。

对两个异步任务的合并可以通过CompletionStage接口的thenCombine()、runAfterBoth()、thenAcceptBoth()三个方法来实现。

一. 合并两个异步任务的结果

1. thenCombine():组合两个异步任务的结果

thenCombine() 方法允许我们同时处理两个异步任务的结果。当这两个任务都完成时,它会执行一个 BiFunction,将两个任务的结果组合起来,并返回一个新的 CompletableFuture,该 CompletableFuture 包含组合后的结果。

使用场景:当你有两个异步任务,且需要同时使用它们的结果来进行某些操作时,thenCombine() 非常适合。

 

示例代码:

java">CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 2);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 3);future1.thenCombine(future2, (result1, result2) -> result1 + result2).thenAccept(result -> System.out.println(result));  // 输出 5**分析**- `future1` 和 `future2` 是两个异步任务,分别返回 `2` 和 `3`。
- `thenCombine()` 会将这两个结果相加,并返回 `5`。
- `thenAccept()` 最终打印出 `5`。

 

例子2:

java">
@Test  
public void thenCombineDemo() throws Exception {  CompletableFuture<Integer> future1 =  CompletableFuture.supplyAsync(new Supplier<Integer>() {  @Override  public Integer get() {  Integer firstStep = 10 + 10;  Print.tco("firstStep outcome is " + firstStep);  return firstStep;  }  });  CompletableFuture<Integer> future2 =  CompletableFuture.supplyAsync(new Supplier<Integer>() {  @Override  public Integer get() {  Integer secondStep = 10 + 10;  Print.tco("secondStep outcome is " + secondStep);  return secondStep;  }  });  CompletableFuture<Integer> future3 = future1.thenCombine(future2,  new BiFunction<Integer, Integer, Integer>() {  @Override  public Integer apply(Integer step1OutCome, Integer step2OutCome) {  return step1OutCome * step2OutCome;  }  });  Integer result = future3.get();  Print.tco(" outcome is " + result);  
}

 

2. runAfterBoth():在两个任务完成后执行无返回值操作

runAfterBoth() 方法会在两个异步任务都完成后执行一个没有返回值的操作。与 thenCombine() 不同,它不关心这两个任务的结果,而是专注于在它们都完成后做某些操作(比如记录日志、更新状态等)。

使用场景:
当你希望在两个异步任务都完成后执行某些副作用操作,但不关心它们的计算结果时,runAfterBoth() 是最理想的选择。

示例代码:

java">CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 2);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 3);future1.runAfterBoth(future2, () -> System.out.println("Both tasks completed"));  // 输出 "Both tasks completed"**分析**- `runAfterBoth()` 会在 `future1` 和 `future2` 都完成后执行,无返回值的操作,这里是打印出 "Both tasks completed"- 重要的是,它不会使用 `future1` 和 `future2` 的结果,而仅在它们都完成时执行某个动作。

 

3. thenAcceptBoth():消费两个任务的结果

thenAcceptBoth() 方法会在两个异步任务都完成后,执行一个 BiConsumer,将这两个任务的结果作为参数传递给消费操作。它适用于在多个任务完成后需要处理这些结果的场景。

使用场景:

当你希望在两个任务都完成时,同时处理它们的结果,但不需要返回任何新的结果时,thenAcceptBoth() 非常适用。

示例代码:

java">CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 2);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 3);future1.thenAcceptBoth(future2, (result1, result2) -> System.out.println("Result1: " + result1 + ", Result2: " + result2));
// 输出 "Result1: 2, Result2: 3"**分析**- `thenAcceptBoth()` 会在 `future1` 和 `future2` 都完成后,执行 `BiConsumer`,并将两个任务的结果传入。
- 这里,我们打印出 `Result1: 2` 和 `Result2: 3`,显示了两个任务的计算结果。

 

二. allOf():等待所有任务完成

allOf() 方法接受多个 CompletableFuture 作为参数,并返回一个新的 CompletableFuture<Void>,该 CompletableFuture 在所有输入的任务都完成时才完成。它通常用于等待多个异步任务全部完成,并且不关心它们的结果。

使用场景:

当你有多个异步任务,并且你希望在它们都完成时继续执行某些操作,而不需要关心每个任务的结果时,可以使用 allOf()。这是一个非常常用的场景,特别是在执行多个独立的异步任务时。

示例代码:

java">CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 2);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 3);
CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> 4);CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2, future3);allOf.thenRun(() -> System.out.println("All tasks are completed"));  // 输出 "All tasks are completed"**分析**- `allOf()` 会等待 `future1`、`future2` 和 `future3` 全部完成。
- `thenRun()` 会在所有任务完成后执行,并输出 "All tasks are completed"- 需要注意的是,`allOf()` 返回的 `CompletableFuture<Void>` 不关心每个任务的计算结果,只关注所有任务是否完成。

thenRun用于在当前 CompletableFuture 完成后执行一个 没有返回值 的操作。它的作用类似于回调机制,但不同于常规的回调函数,thenRun() 不接受任务的结果作为输入,它只是执行一个副作用操作(例如打印日志、更新状态等)。

 

例子2:

java">@Test  
public void allOfDemo() throws Exception {  CompletableFuture<Void> future1 =  CompletableFuture.runAsync(() -> Print.tco("模拟异步任务1"));  CompletableFuture<Void> future2 =  CompletableFuture.runAsync(() -> Print.tco("模拟异步任务2"));  CompletableFuture<Void> future3 =  CompletableFuture.runAsync(() -> Print.tco("模拟异步任务3"));  CompletableFuture<Void> future4 =  CompletableFuture.runAsync(() -> Print.tco("模拟异步任务4"));  CompletableFuture<Void> all =  CompletableFuture.allOf(future1, future2, future3, future4);  all.join();  
}

join() 方法与 get() 类似,但是它不会抛出 InterruptedExceptionExecutionException。相反,如果任务失败,join() 会抛出 CompletionException,这意味
着我们需要通过 join() 等待所有任务完成,并且如果有任务失败,CompletionException 会包装抛出异常。

 


http://www.ppmy.cn/devtools/155603.html

相关文章

计算机视觉和图像处理

计算机视觉与图像处理的最新进展 随着人工智能技术的飞速发展&#xff0c;计算机视觉和图像处理作为其中的重要分支&#xff0c;正逐步成为推动科技进步和产业升级的关键力量。 一、计算机视觉的最新进展 计算机视觉&#xff0c;作为人工智能的重要分支&#xff0c;主要研究如…

MATLAB中extractAfter函数用法

目录 语法 说明 示例 选择子字符串后的文本 使用模式提取路径后的文件名 选择指定位置后的子字符串 选择字符向量中位置之后的文本 extractAfter函数的用法是提取指定位置后的子字符串。 语法 newStr extractAfter(str,pat) newStr extractAfter(str,pos) 说明 new…

《DeepSeek手机版:开启AI移动新时代》

DeepSeek 手机版爆火&#xff1a;现象与背景 在当今数字化时代&#xff0c;AI 技术的发展日新月异&#xff0c;如同一股汹涌澎湃的浪潮&#xff0c;深刻地改变着我们的生活。而在这股浪潮中&#xff0c;DeepSeek 手机版宛如一颗璀璨的新星&#xff0c;迅速崛起&#xff0c;引发…

【Linux】从硬件到软件了解进程

个人主页~ 从硬件到软件了解进程 一、冯诺依曼体系结构二、操作系统三、操作系统进程管理1、概念2、PCB和task_struct3、查看进程4、通过系统调用fork创建进程&#xff08;1&#xff09;简述&#xff08;2&#xff09;系统调用生成子进程的过程〇提出问题①fork函数②父子进程关…

git:恢复纯版本库

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

【设计模式-行为型】解释器模式

一、什么是解释器模式 到此为止23种解释器都将介绍完毕&#xff0c;下面说最后一个设计模式解释器模式。有许多谍战剧大家都会看过&#xff0c;在谍战剧中经常会有一些主要情节需要进行加密传递。间谍会接受或者发送加密信息。比如用收音机接收指令或者消息。间谍会根据特定的解…

使用jmeter进行压力测试

使用jmeter进行压力测试 jmeter安装 官网安装包下载&#xff0c;选择二进制文件&#xff0c;解压 tar -xzvf apache-jmeter-x.tgz依赖jdk安装 yum install java-1.8.0-openjdk环境变量配置&#xff0c;修改/etc/profile文件&#xff0c;添加以下内容 export JMETER/opt/apa…

C++ 堆栈分配的区别

这两种声明方式有什么区别 1.使用 new 关键字动态分配内存 动态分配&#xff1a;使用 new 关键字会在堆&#xff08;heap&#xff09;上分配内存&#xff0c;并返回一个指向该内存位置的指针。生命周期&#xff1a;对象的生命周期不会随着声明它的作用域结束而结束&#xff0…