当前位置: 首页 > article >正文

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

文章目录

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

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

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

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

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

方法签名:

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

使用场景:

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

示例代码:

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

方法签名:

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

使用场景:

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

示例代码:

@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。该方法适用于只需要消费结果的场景。

方法签名:

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

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

示例代码:

@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 操作,适用于消费任务的结果。

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

相关文章:

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

文章目录 一. applyToEither : 拿到第一个任务结束的结果二. runAfterEither &#xff1a;第一个任务完成后执行副作用三. acceptEither&#xff1a;消费第一个任务的结果四. 三种接口总结 对于两个异步任务&#xff0c;我们有时希望在其中一个任务完成时立即执行某些操作&…...

4 [危机13小时追踪一场GitHub投毒事件]

事件概要 自北京时间 2024.12.4 晚间6点起&#xff0c; GitHub 上不断出现“幽灵仓库”&#xff0c;仓库中没有任何代码&#xff0c;只有诱导性的病毒文件。当天&#xff0c;他们成为了 GitHub 上 star 增速最快的仓库。超过 180 个虚假僵尸账户正在传播病毒&#xff0c;等待不…...

变量和常量

一.变量 1.标准声明 var 变量名 变量类型 变量声明行末不需要分号 2..批量声明 package main import "fmt" func main(){var(a string b int c boold float32)}3.变量的初始化 var a int 10 var b float321.1 4.类型推导 var name"tom" var age18 fmt.Pr…...

大模型概述(方便不懂技术的人入门)

1 大模型的价值 LLM模型对人类的作用&#xff0c;就是一个百科全书级的助手。有多么地百科全书&#xff0c;则用参数的量来描述&#xff0c; 一般地&#xff0c;大模型的参数越多&#xff0c;则该模型越好。例如&#xff0c;GPT-3有1750亿个参数&#xff0c;GPT-4可能有超过1万…...

流浪 Linux: 外置 USB SSD 安装 ArchLinux

注: ArchLinux 系统为滚动更新, 变化很快, 所以本文中的安装方法可能很快就过时了, 仅供参考. 实际安装时建议去阅读官方文档. 最近, 突然 (也没有那么突然) 有了一大堆 PC: 4 个笔记本, 2 个台式主机 (M-ATX 主板), 1 个小主机 (迷你主机). 嗯, 多到用不过来. 但是, 窝又不能…...

Hot100之子串

560和为K的子数组 题目 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列 思路解析 ps&#xff1a;我们的presum【0】就是0&#xff0c;如果没有这个0的话我们的第一个元素就无法减去上…...

网络工程师 (11)软件生命周期与开发模型

一、软件生命周期 前言 软件生命周期&#xff0c;也称为软件开发周期或软件开发生命周期&#xff0c;是指从软件项目的启动到软件不再被使用为止的整个期间。这个过程可以细分为多个阶段&#xff0c;每个阶段都有其特定的目标、任务和产出物。 1. 问题定义与需求分析 问题定义…...

(三)QT——信号与槽机制——计数器程序

目录 前言 信号&#xff08;Signal&#xff09;与槽&#xff08;Slot&#xff09;的定义 一、系统自带的信号和槽 二、自定义信号和槽 三、信号和槽的扩展 四、Lambda 表达式 总结 前言 信号与槽机制是 Qt 中的一种重要的通信机制&#xff0c;用于不同对象之间的事件响…...

从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础图形库实现)

目录 基础图形库的抽象 抽象图形 抽象点 设计我们的抽象 实现我们的抽象 测试 抽象线 设计我们的抽象 实现我们的抽象 绘制垂直的和水平的线 使用Bresenham算法完成任意斜率的绘制 绘制三角形和矩形 矩形 三角形 实现 绘制圆&#xff0c;圆弧和椭圆 继续我们的…...

hot100_21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出&#xff1a;[…...

安全防护前置

就业概述 网络安全工程师/安全运维工程师/安全工程师 安全架构师/安全专员/研究院&#xff08;数学要好&#xff09; 厂商工程师&#xff08;售前/售后&#xff09; 系统集成工程师&#xff08;所有计算机知识都要会一点&#xff09; 学习目标 前言 网络安全事件 蠕虫病毒--&…...

01-六自由度串联机械臂(ABB)位置分析

ABB工业机器人&#xff08;IRB2600&#xff09;如下图所示&#xff08;d1444.8mm&#xff0c;a1150mm&#xff0c;a2700mm&#xff0c;a3115mm&#xff0c;d4795mm&#xff0c;d685mm&#xff09;&#xff0c;利用改进DH法建模&#xff0c;坐标系如下所示&#xff1a; 利用改进…...

JVM运行时数据区域-附面试题

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域 有各自的用途&#xff0c;以及创建和销毁的时间&#xff0c;有的区域随着虚拟机进程的启动而一直存在&#xff0c;有些区域则是 依赖用户线程的启动和结束而建立和销毁。 1. 程序计…...

Java线程池与Future_优化并发任务执行

1. 引言 1.1 并发编程的重要性 并发编程是现代软件开发中的关键部分,特别是在处理高并发、大数据和分布式系统时。通过并发编程,可以充分利用多核处理器的计算能力,提高系统的吞吐量和响应速度。 1.2 线程池与Future的作用 线程池:提供了对线程资源的有效管理和复用,减…...

HTML(快速入门)

欢迎大家来到我的博客~欢迎大家对我的博客提出指导&#xff0c;有错误的地方会改进的哦~点击这里了解更多内容 目录 一、前言二、HTML基础2.1 什么是HTML?2.2 认识HTML标签2.2.1 HTML标签当中的基本结构2.2.2 标签层次结构 2.3 HTML常见标签2.3.1 标题标签2.3.2 段落标签2.3.3…...

《苍穹外卖》项目学习记录-Day10订单状态定时处理

利用Cron表达式生成器生成Cron表达式 1.处理超时订单 查询订单表把超时的订单查询出来&#xff0c;也就是订单的状态为待付款&#xff0c;下单的时间已经超过了15分钟。 //select * from orders where status ? and order_time < (当前时间 - 15分钟) 遍历集合把数据库…...

WebForms SortedList 深度解析

WebForms SortedList 深度解析 引言 在Web开发领域,对于数据结构的理解与应用至关重要。其中,SortedList类在WebForms中是一个常用的数据结构,它能够帮助开发者高效地管理有序数据集合。本文将深入解析SortedList类在WebForms中的应用,包括其基本概念、常用方法、性能特点…...

AJAX综合案例——图书管理

黑马程序员视频地址&#xff1a; AJAX-Day02-10.案例_图书管理AJAX-Day02-10.案例_图书管理_总结_V1.0是黑马程序员前端AJAX入门到实战全套教程&#xff0c;包含学前端框架必会的&#xff08;ajaxnode.jswebpackgit&#xff09;&#xff0c;一套全覆盖的第25集视频&#xff0c…...

如何在Windows、Linux和macOS上安装Rust并完成Hello World

如何在Windows、Linux和macOS上安装Rust并完成Hello World 如果你刚刚开始学习Rust&#xff0c;第一步就是安装Rust并运行你的第一个程序&#xff01;本文将详细介绍如何在Windows、Linux和macOS上安装Rust&#xff0c;并编写一个简单的“Hello, World!”程序。 1. 安装Rust …...

使用 Redis Streams 实现高性能消息队列

1. 引言 在后端开发中&#xff0c;消息队列是一个常见的组件&#xff0c;主要用于解耦系统、提高吞吐量以及实现异步处理。常见的消息队列包括 Kafka、RabbitMQ 以及 ActiveMQ&#xff0c;但 Redis Streams 作为 Redis 5.0 引入的新特性&#xff0c;也提供了一种高效、轻量的消…...

30.Word:设计并制作新年贺卡以及标签【30】

目录 NO1.2 NO3邮件合并-信函 NO4邮件合并-标签​ NO1.2 另存为/F12&#xff1a;考生文件夹&#xff1a;Word.docx布局→页面设置对话框→页边距&#xff1a;上下左右→纸张&#xff1a;宽度/高度&#xff08;先调页边距&#x1f197;&#xff09;设计→页面颜色→填充效果→…...

Nginx开发01:基础配置

一、下载和启动 1.下载、使用命令行启动&#xff1a;Web开发&#xff1a;web服务器-Nginx的基础介绍&#xff08;含AI文稿&#xff09;_nginx作为web服务器,可以承担哪些基本任务-CSDN博客 注意&#xff1a;我配置的端口是81 2.测试连接是否正常 访问Welcome to nginx! 如果…...

数据分析系列--⑨RapidMiner训练集、测试集、验证集划分

一、数据集获取 二、划分数据集 1.导入和加载数据 2.数据集划分 2.1 划分说明 2.2 方法一 2.3 方法二 一、数据集获取 点击下载数据集 此数据集包含538312条数据. 二、划分数据集 1.导入和加载数据 2.数据集划分 2.1 划分说明 2.2 方法一 使用Filter Example Range算子. …...

C基础寒假练习(6)

一、终端输入行数&#xff0c;打印倒金字塔 #include <stdio.h> int main() {int rows;printf("请输入倒金字塔的行数: ");scanf("%d", &rows);for (int i rows; i > 0; i--) {// 打印空格for (int j 0; j < rows - i; j) {printf(&qu…...

mysqldump+-binlog增量备份

注意&#xff1a;二进制文件删除必须使用help purge 不可用rm -f 会崩 一、概念 增量备份&#xff1a;仅备份上次备份以后变化的数据 差异备份&#xff1a;仅备份上次完全备份以后变化的数据 完全备份&#xff1a;顾名思义&#xff0c;将数据完全备份 其中&#xff0c;…...

《DeepSeek R1:大模型最简安装秘籍》

DeepSeek R1&#xff1a;AI 大模型界的新起之秀 在人工智能的璀璨星空中&#xff0c;大模型如繁星般闪耀&#xff0c;而 DeepSeek R1 无疑是其中一颗冉冉升起的新星&#xff0c;自问世以来便吸引了全球的目光&#xff0c;在人工智能领域占据了重要的一席之地。 从性能表现上看…...

FLTK - FLTK1.4.1 - demo - bitmap

文章目录 FLTK - FLTK1.4.1 - demo - bitmap概述笔记END FLTK - FLTK1.4.1 - demo - bitmap 概述 // 功能 : 演示位图数据在按钮上的显示 // * 以按钮为范围或者以窗口为范围移动 // * 上下左右, 文字和图像的相对位置 // 失能按钮&#xff0c;使能按钮 // 知识点 // FLTK可…...

数据库优化:提升性能的关键策略

1. 引言 在后端开发中&#xff0c;数据库的性能直接影响系统的稳定性和响应速度。随着业务增长&#xff0c;数据库查询变慢、负载过高等问题可能会影响用户体验。 本文将介绍数据库优化的关键策略&#xff0c;包括索引优化、查询优化、分库分表、缓存机制等&#xff0c;并结合…...

【Leetcode 每日一题】119. 杨辉三角 II

问题背景 给定一个非负索引 r o w I n d e x rowIndex rowIndex&#xff0c;返回「杨辉三角」的第 r o w I n d e x rowIndex rowIndex 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 数据约束 0 ≤ r o w I n d e x ≤ 33 0 \le rowIndex \le 33 …...

Java小白入门教程:HashSet

目录 一、定义 二、作用 1、存储唯一元素 2、快速查找 3、去除重复 三、使用场景 1、当你需要存储一系列唯一的元素&#xff0c;并且不关心元素的顺序时。 2、当你需要快速判断一个元素是否存在于集合中时。 四、语法及示例 1、创建HashSet 2、添加元素 3、检查元素…...