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

Java CompletableFuture:你真的了解它吗?

在这里插入图片描述

文章目录

    • 1 什么是 CompletableFuture?
    • 2 如何正确使用 CompletableFuture 对象?
    • 3 如何结合回调函数处理异步任务结果?
    • 4 如何组合并处理多个 CompletableFuture?

1 什么是 CompletableFuture?

CompletableFuture 是 Java 8 引入的一个强大的异步编程工具。允许以声明式的方式处理异步任务的结果,避免了传统回调和手动管理线程的复杂性。

CompletableFuture 可以组合和链式调用,高效地利用多核处理器的能力,并且减少了传统并发编程中常见的竞态条件和死锁等问题。

在日常开发中,经常需要处理那些可能耗时的任务,比如网络请求、数据库查询或者复杂的计算。使用 CompletableFuture,可以告诉程序如何在后台执行这些任务,然后在任务完成后执行特定的操作。

可以想象一下,CompletableFuture 就像是一条可以穿越时间的信使,你可以把一项任务托付给它,然后继续做其他事情。当任务完成时,它会及时将结果送回来,让你可以立即处理。这样,你就不必在等待任务完成的过程中浪费时间,而是可以更高效地利用自己的资源。

2 如何正确使用 CompletableFuture 对象?

CompletableFuture 可以以一种非阻塞的方式执行异步任务,并能够在任务完成后立即得到通知。通过链式调用的方式,可以很方便地组合多个异步操作,处理它们的结果或者异常。

通过 CompletableFuture.supplyAsync() 方法创建一个 CompletableFuture 对象,并指定一个需要异步执行的任务:

//  Supplier 函数会在一个新的线程上异步执行
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {// 模拟一个耗时操作,如从数据库中读取数据try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}return "异步任务完成";
});

通过在 CompletableFuture 上添加一些操作,比如处理任务的结果或者处理任务执行过程中可能发生的异常:

// thenAccept()方法接收一个 Consumer 函数
future.thenAccept(result -> {System.out.println("任务完成,结果为:" + result);
}).exceptionally(ex -> {System.out.println("任务出现异常:" + ex.getMessage());return null;
});

3 如何结合回调函数处理异步任务结果?

结合回调函数处理异步任务结果的过程可以比作在等待一份重要的快递时安排一个通知服务。这个通知服务就是回调函数,它会在快递送达时通知你,或者在处理完成后执行特定的操作。

在 Java 的 CompletableFuture 中,这种模式可以通过 supplyAsync()thenApply()thenAccept()handle() 方法来实现。

创建一个异步任务时,使用 CompletableFuture.supplyAsync() 可以启动一个任务,这个任务在后台线程中执行,直到它完成。假设有一个任务需要从远程服务器获取数据:

// supplyAsync() 方法接收一个 Supplier 函数,这个函数会在后台线程中运行,并返回一个结果
// 结果会被封装在 CompletableFuture 对象中,等待进一步处理
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {// 模拟从远程服务器获取数据try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}return "数据已成功获取";
});

使用 thenApply() 方法,可以在异步任务完成后,对结果进行转换。这个方法接收一个 Function 函数,这个函数会接收任务的结果,并返回一个新结果。比如,将获取的数据进行处理:

// thenApply() 方法将原始数据转换为大写形式
// 处理后的结果会成为新的 CompletableFuture 对象的结果
CompletableFuture<String> processedFuture = future.thenApply(result -> {// 对结果进行处理return result.toUpperCase();
});

为了执行一个操作而不关心处理的结果,可以使用 thenAccept() 方法。这个方法接收一个 Consumer 函数,它处理任务完成时的结果,可以在异步任务完成时执行一些操作,比如日志记录或通知用户。例如,将结果打印到控制台:

// 在任务完成后会调用传入的 Consumer 函数,并将结果传递给它
future.thenAccept(result -> {System.out.println("任务完成,结果是:" + result);
});

在任务执行过程中,可能会遇到异常。handle() 方法可以用来处理这些异常,它接收一个 BiFunction 函数,这个函数接收结果和异常(如果有的话),并返回一个处理后的结果。例如:

// handle() 方法检查是否有异常发生
// 如果有异常,它会处理异常并返回一个默认的结果
// 如果没有异常,它会处理正常的结果
CompletableFuture<String> handledFuture = future.handle((result, ex) -> {if (ex != null) {// 处理异常System.out.println("任务发生错误:" + ex.getMessage());return "错误处理结果";}// 处理正常结果return result.toLowerCase();
});

对于这四种回调函数,可以使得异步任务的结果处理变得灵活而强大。通过结合使用不同的回调函数,可以对异步任务的结果进行多种操作,保证程序在处理复杂任务时仍然保持清晰和高效。

4 如何组合并处理多个 CompletableFuture?

组合和处理多个 CompletableFuture 可以让并发任务变得更加灵活和高效。设想有多个任务需要并行执行,然后将它们的结果结合起来进行进一步处理。

在进行组合时,最基本的方法之一是将多个 CompletableFuture 的结果合并。比如,有两个任务需要并行完成,获取两个不同的数据源,然后将这两个结果结合起来。

可以使用 thenCombine() 方法,它接收两个 CompletableFuture 和一个合并函数,两个 CompletableFuture 必须在相同的线程池中执行。

假设有两个任务分别从不同的 API 获取数据:

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {// 模拟从第一个 API 获取数据return "数据1";
});CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {// 模拟从第二个 API 获取数据return "数据2";
});

为了将这两个结果结合起来,可以使用 thenCombine()

// thenCombine() 方法接收两个 CompletableFuture 和一个函数,这个函数将两个任务的结果合并成一个结果
// 最终的结果是将两个字符串连接在一起
CompletableFuture<String> combinedFuture = future1.thenCombine(future2, (result1, result2) -> {// 将两个结果结合成一个return result1 + " 和 " + result2;
});

另一个有用的方法是 allOf()方法。当有多个任务需要并行执行,并且在所有任务完成后执行某个操作时,allOf() 非常有用。它接收一个 CompletableFuture 数组,并在所有这些 CompletableFuture 完成时触发。可以用来等待多个异步任务完成,然后执行某个操作:

CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(future1, future2);

要获取所有任务的结果,可以在 allOf() 的结果上添加一个回调函数:

// thenRun() 方法会在所有任务完成后执行,它不需要处理结果,只是执行某个操作
allOfFuture.thenRun(() -> {// 处理所有任务完成后的操作try {String result1 = future1.get();String result2 = future2.get();System.out.println("任务1的结果: " + result1);System.out.println("任务2的结果: " + result2);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}
});

如果有任务依赖于另一个任务的结果,可以使用 thenCompose()方法。这种情况下,第二个任务会在第一个任务完成后开始执行。thenCompose() 方法接收一个返回 CompletableFuture 的函数,然后将这两个 CompletableFuture 链接起来:

CompletableFuture<String> future3 = future1.thenCompose(result1 -> {// 使用第一个任务的结果来创建新的 CompletableFuturereturn CompletableFuture.supplyAsync(() -> result1 + " 处理完成");
});

对于这些方法来说,它们为处理多个异步任务提供了强大的工具,使得并发编程更加高效和灵活。通过合理使用这些方法,可以实现复杂的异步任务组合和处理逻辑,确保程序的高效执行。

世界会向那些有目标和远见的人让路

相关文章:

Java CompletableFuture:你真的了解它吗?

文章目录 1 什么是 CompletableFuture&#xff1f;2 如何正确使用 CompletableFuture 对象&#xff1f;3 如何结合回调函数处理异步任务结果&#xff1f;4 如何组合并处理多个 CompletableFuture&#xff1f; 1 什么是 CompletableFuture&#xff1f; CompletableFuture 是 Ja…...

5个免费在线 AI 绘画网站推荐,附100+提示词!

在数字化时代&#xff0c;艺术创作与人工智能的结合已带来前所未有的创新体验。AI 绘画技术&#xff0c;基于先进的人工智能算法&#xff0c;为艺术创作提供了全新的视角和工具。当前&#xff0c;多个免费在线AI绘画平台应运而生&#xff0c;为创作者们提供了丰富的灵感和创作机…...

C++基础语法:while的使用

前言 "打牢基础,万事不愁" .C的基础语法的学习."学以致用,边学边用",编程是实践性很强的技术,在运用中理解,总结. 引入 while的使用是编写代码的基础内容.笔者的记忆力已不如以前,最近遇到了还花了不少功夫,可见是掌握地不够牢固.所以对while的思路和内容…...

鹏哥C语言自定义笔记重点(29-)

29.函数指针数组 30.void指针是不能直接解引用&#xff0c;也不能-整数。 void*是无具体类型的指针&#xff0c;可以接受任何类型的地址。 31.qsort:使用快速排序的思想实现一个排序函数(升序) 32. 33.地址的字节是4/8 34.char arr[]{a,b} sizeof(arr[0]1)答案是4&#xff0…...

代码随想录算法训练营第六十天 | dijkstra(堆优化版)、Bellman_ford 算法精讲

一、dijkstra&#xff08;堆优化版&#xff09; 题目连接&#xff1a;47. 参加科学大会&#xff08;第六期模拟笔试&#xff09; (kamacoder.com) 文章讲解&#xff1a;代码随想录 (programmercarl.com)——dijkstra&#xff08;堆优化版&#xff09; 二、Bellman_ford 算法精讲…...

boost::asio 库版本,C/C++代码编译兼容性

1、boost::asio::spawn 开启有栈&#xff08;stackful&#xff09;协同程序&#xff0c;版本改进及限制 > boost_1_80 版本应采用以下方式。 auto f [self, this](const boost::asio::yield_context& y) noexcept {bool success_ do_handshake(y);if (!success_) {clo…...

前端开发的项目导入方法与应用

前端项目启动问题归集&#xff1a; 由于前端的项目对于npm的版本有要求&#xff0c;需要将其升级到20&#xff0c;所以必要的时候通过nvm&#xff0c;或者直接下载最新的安装包进行npm覆盖安装。在项目目录中应用npm i安装node_modules&#xff0c;如果没有正常安装的话&#…...

C++:模拟实现string

前言&#xff1a; 为了更好的理解string底层的原理&#xff0c;我们将模拟实现string类中常用的函数接口。为了与std里的string进行区分&#xff0c;所以用命名空间来封装一个自己的strin类。 string.h #pragma once #define _CRT_SECURE_NO_WARNINGS 1#include<iostream&…...

浅谈Kafka(一)

浅谈Kafka&#xff08;一&#xff09; 文章目录 浅谈Kafka&#xff08;一&#xff09;Kafa的设计是什么样的数据传输的事务定义消息队列的应用场景Kafka怎么样判断节点是否存活Kafka的消息是采用pull模式还是push模式Kafka在磁盘上的消息格式Kafka高效文件存储设计特点Kafka与传…...

Redis7基础篇(八)

redis集群 是什么 能干吗 集群算法-分片-槽位slot redis集群的槽位slot redis集群的分片 分片和槽位的优势 槽位映射的解决方案 上面的三个方案分别对应了小厂 中厂 大厂 哈希槽取余分区 缺点 一致性哈希算法分区 小总结 哈希槽分区 经典面试题 这里说的redis是ap而不是cp的 …...

Tauri简介

在Tauri应用中&#xff0c;Rust和前端&#xff08;通常是基于Web技术如React、Vue或Angular&#xff09;之间的交互是一个核心特性&#xff0c;它允许开发者利用Rust的强大功能和性能&#xff0c;同时保持前端开发的灵活性和丰富的生态系统。这种交互主要通过Tauri提供的API桥接…...

JavaWeb——MVC架构模式

一、概述: MVC(Model View Controller)是软件工程中的一种 软件架构模式 &#xff0c;它把软件系统分为模型、视图和控制器三个基本部分。用一种业务逻辑、数据、界面显示分离的方法组织代码&#xff0c;将业务逻辑聚集到一个部件里面&#xff0c;在改进和个性化定制界面及用户…...

Excel求和方法之

一 SUM&#xff08;&#xff09;&#xff0c;选择要相加的数,回车即可 二 上面的方法还不够快。用下面这个 就成功了 三 还有一种一样快的 选中之后&#xff0c;按下Alt键和键&#xff08;即Alt&#xff09;...

Windows Server 域控制服务器安装及相关使用

目录 1.将客户机加入域 2.安装域控制器 3.新建域用户 4.设置用户登录时间&#xff0c;账户过期时间 5.软件分发 ​编辑 6.换壁纸 7.OU与GPO的概念 域为集中控制&#xff0c;拿下域控是拿下目标的关键 以Windows Server 2022为例 1.将客户机加入域 前提&#xff1a;客…...

linux基础命令(超级详细)

Linux 系统提供了丰富的命令行工具&#xff0c;用于各种文件操作、系统管理和网络配置等任务。以下是一些常用的 Linux 基础命令&#xff1a; 一、 文件和目录操作 1. ls: 列出目录内容 ls 列出当前目录的文件和目录 ls -l 以长格式列出文件和目录&#xff0c;包…...

大模型笔记之-XTuner微调个人小助手认知

前言 使用XTuner 微调个人小助手认知 一、下载模型 #安装魔搭依赖包 pip install modelscope新建download.py内容如下 其中Shanghai_AI_Laboratory/internlm2-chat-1_8b是魔搭对应的模型ID cache_dir/home/aistudio/data/model’为指定下载到本地的目录 from modelscope im…...

用TensorFlow实现线性回归

说明 本文采用TensorFlow框架进行讲解&#xff0c;虽然之前的文章都采用mxnet&#xff0c;但是我发现tensorflow提供了免费的gpu可供使用&#xff0c;所以果断开始改为tensorflow&#xff0c;若要实现文章代码&#xff0c;可以使用colaboratory进行运行&#xff0c;当然&#…...

IT计算机软件系统类毕业论文结构指南:从标题到结论的全景视角

一、背景 在快速发展的IT和人工智能领域&#xff0c;毕业论文不仅是学术研究的重要成果&#xff0c;也展示了学生掌握新技术和应用的能力。随着大数据和智能系统的复杂性增加&#xff0c;毕业设计&#xff08;毕设&#xff09;的论文章节安排变得尤为关键。一个结构清晰、内容详…...

leetcode27:移除元素(正解)

移除元素 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数量为 k&#xff0c;要通过此题&#xff0c;您需要执行以下操作&#xf…...

docker部署nginx--(部署静态文件和服务)

文档参考 1、http://testingpai.com/article/1649671014266 2、下载nginx docker pull nginx:alpine 然后启动nginx&#xff0c; docker run --rm -it -p 9192:80 nginx:alpine /bin/sh 关闭容器后&#xff0c;自动删除该容器 进入后&#xff0c;启动nginx, nginx进行curl h…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

智能AI电话机器人系统的识别能力现状与发展水平

一、引言 随着人工智能技术的飞速发展&#xff0c;AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术&#xff0c;在客户服务、营销推广、信息查询等领域发挥着越来越重要…...