【异步编程】CompletableFuture:异步任务的选择(执行最快的)执行

news/2025/2/2 19:31:04/

文章目录

    • 一. applyToEither : 拿到第一个任务结束的结果
    • 二. runAfterEither :第一个任务完成后执行副作用
    • 三. acceptEither:消费第一个任务的结果
    • 四. 三种接口总结

对于两个异步任务,我们有时希望在其中一个任务完成时立即执行某些操作,而不必等待其他任务的完成。为了实现这一需求,CompletionStage 提供了几个有用的方法,其中包括 applyToEither()runAfterEither()acceptEither()

CompletableFuture对异步任务的选择执行不是按照某种条件进行选择的,而是按照执行速度进行选择的:前面两个并行任务,谁的结果返回速度快,谁的结果将作为第三步任务的输入。

一. applyToEither : 拿到第一个任务结束的结果

applyToEither() 方法允许我们在两个异步任务中选择一个已经完成的任务,并对其结果应用一个函数。无论哪个任务先完成,都会执行传入的 Function 操作,并返回一个新的 CompletableFuture

方法签名:

java">public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, ? extends U> fn)- **`other`**:另一个 `CompletionStage`,与当前任务并行执行。
- **`fn`**(两个任务中)当第一个任务完成时应用的函数。

使用场景:

applyToEither() 方法非常适合于需要在多个异步任务中选择一个最快完成的任务结果并进行处理的场景。

示例代码:

java">@Test  
public void applyToEitherDemo2() throws Exception {  CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {  try {  Thread.sleep(500);  } catch (InterruptedException e) {  Thread.currentThread().interrupt();  }  return 2;  });  CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {  try {  Thread.sleep(100);  } catch (InterruptedException e) {  Thread.currentThread().interrupt();  }  return 3;  });  CompletableFuture<Integer> result = future1.applyToEither(future2, (result1) -> {  System.out.println("第一个完成的任务的结果: " + result1);  return result1 * 2;  // 对结果进行处理  });  Integer integer = result.get();  System.out.println(integer);  }

分析

  • future1future2 两个异步任务并行执行。
  • applyToEither() 会选择第一个完成的任务,并将其结果传递给 fn
    这里是对结果进行乘以 2 的操作。
  • 如果 future2 完成更快(如上例中的500ms),则会输出 "第一个完成的任务的结果: 3" 并返回 6

 

二. runAfterEither :第一个任务完成后执行副作用

runAfterEither() 方法与 applyToEither() 类似,但它不关心任务的结果。它只在其中一个任务完成时执行一个没有返回值的操作。它适用于需要在多个任务中选择一个最先完成的任务并执行某个副作用操作(如打印日志、更新状态等)的场景。

方法签名:

java">public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,Runnable action)- **`other`**:另一个 `CompletionStage`。
- **`action`**:当第一个任务完成时执行的 `Runnable` 操作。
- **返回类型**:返回一个新的 `CompletableFuture<Void>`,表示操作完成。

使用场景:

runAfterEither() 方法适用于当你只关心某个任务完成后执行某些副作用操作,而不需要处理任务的结果时。

示例代码:

java">@Test  
public void test() {  CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  Thread.currentThread().interrupt();  }  return 2;  });  CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {  try {  Thread.sleep(500);  } catch (InterruptedException e) {  Thread.currentThread().interrupt();  }  return 3;  });  future1.runAfterEither(future2, () -> System.out.println("第一个完成的任务已经结束"));  
}

分析

  • future1future2 分别执行两个异步任务。
  • runAfterEither() 会在第一个任务完成后执行 Runnable 操作,这里是打印 "第一个完成的任务已经结束"
  • 无论哪个任务先完成,都会触发打印操作,而不关心它们的计算结果。

 

三. acceptEither:消费第一个任务的结果

acceptEither() 方法与 applyToEither() 方法类似,不同之处在于它接受一个 BiConsumer 来消费完成任务的结果,而不是返回一个新的 CompletableFuture。该方法适用于只需要消费结果的场景。

方法签名:

java">public void acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)- **`other`**:另一个 `CompletionStage`。
- **`action`**:当第一个任务完成时执行的 `Consumer` 操作。
- **返回类型**:该方法没有返回值,它会直接消费任务的结果。

使用场景:
acceptEither() 方法适用于当我们只关心任务的结果并需要消费(如打印、记录日志等)时,但不需要返回任何新的 CompletableFuture

示例代码:

java">@Test  
public void test2() {  CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  Thread.currentThread().interrupt();  }  return 2;  });  CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {  try {  Thread.sleep(500);  } catch (InterruptedException e) {  Thread.currentThread().interrupt();  }  return 3;  });  //使用 join() 阻塞直到操作完成  future1.acceptEither(future2, (result) -> System.out.println("第一个完成的任务的结果: " + result)).join();  }**分析**- `future1` 和 `future2` 分别执行异步任务。
- `acceptEither()` 会选择第一个完成的任务,并将其结果传递给 `Consumer` 进行处理。在这里,我们将结果打印出来。
- 输出会是 `"第一个完成的任务的结果: 3"`,因为 `future2` 在500ms时先完成。

在测试代码中,通常会使用 join() 来确保异步任务完成后,主线程再退出,避免出现异步回调没有被执行的情况。

四. 三种接口总结

  • applyToEither():在两个异步任务中选择一个最先完成的任务,并对其结果应用一个函数,返回一个新的 CompletableFuture
  • runAfterEither():在两个任务中选择一个最先完成的任务,不关心任务的结果,只执行一个 Runnable 操作。
  • acceptEither():在两个任务中选择一个最先完成的任务,并对其结果执行一个 Consumer 操作,适用于消费任务的结果。

这三个方法提供了灵活的工具,可以帮助我们在多个并行任务中选择一个最先完成的任务,并根据需求对其结果进行处理或执行副作用操作。掌握这些方法,可以让我们更高效地进行异步编程和任务调度。


http://www.ppmy.cn/news/1568772.html

相关文章

智联出行公司布局中国市场,引领绿色出行新潮流

近日&#xff0c;智联共享科技有限公司&#xff08;智联出行ZSTL&#xff09;正式入驻中国香港市场&#xff0c;开启中国地区“合伙人”战略部署&#xff0c;其线上服务平台也于同日上线。 作为共享单车领域的先行者&#xff0c;智联出行公司此举标志着其全球化布局的重要进展&…

数据结构:队列篇

图均为手绘,代码基于vs2022实现 系列文章目录 数据结构初探: 顺序表 数据结构初探:链表之单链表篇 数据结构初探:链表之双向链表篇 链表特别篇:链表经典算法问题 数据结构:栈篇 文章目录 系列文章目录前言一.队列的概念和结构1.1概念一、动态内存管理优势二、操作效率与安全性…

【memgpt】letta 课程5:可编程的agent内存

Programming Agent Memory 基本上是内存和内存块的部分。其中每个块都对应于某种字符限制。限制定义了当前块可以用掉多少上下文窗口。该块还有一个标签:

谭浩强C语言程序设计(3) 7章

1、递归实现N的阶乘 c复制 #include <cstdio> // 包含标准输入输出库// 计算n的阶乘 int total 0; // 定义全局变量total用于存储阶乘结果// 递归函数计算阶乘 int fac(int a){// 如果输入的数小于0&#xff0c;输出错误信息if (a < 0){printf("%d < 0,err…

使用 Docker + Nginx + Certbot 实现自动化管理 SSL 证书

使用 Docker Nginx Certbot 实现自动化管理 SSL 证书 在互联网安全环境日益重要的今天&#xff0c;为站点或应用部署 HTTPS 已经成为一种常态。然而&#xff0c;手动申请并续期证书既繁琐又容易出错。本文将以 Nginx Certbot 为示例&#xff0c;基于 Docker 容器来搭建一个…

把本地搭建的hexo博客部署到自己的服务器上

配置远程服务器的git 安装git 安装依赖工具包 yum install -y curl-devel expat-devel gettext-devel openssl-devel zlib-devel安装编译工具 yum install -y gcc perl-ExtUtils-MakeMaker package下载git&#xff0c;也可以去官网下载了传到服务器上 wget https://www.ke…

本地部署DeepSeekp R1教程

目录 一.打开ollama官网&#xff0c;下载安装 1.下载完成双击安装程序 2.winr 输入cmd打开命令行输入命令 查看是否安装成功 二.部署DeepSeek R1模型 1. 下载模型&#xff1a;终端输入 (根据你的显存大小选择版本&#xff0c;16g就可以选择14b/32b)**电脑配置很低的话选…

Go-并行编程新手指南

Go 并行编程新手指南 在Go语言中&#xff0c;并行编程是充分利用多核CPU资源、提升程序性能的重要手段。它的核心概念包括goroutine和channel&#xff0c;这些特性使得Go在处理并发任务时表现出色。 goroutine&#xff1a;轻量级的并发执行单元 goroutine是Go并行编程的基础…