【Java并发编程】使用CompletableFuture最佳实践

文章目录

  • 1. 什么是CompletableFuture
  • 2. 为什么需要CompletableFuture
  • 3. 使用CompletableFuture
    • 创建类
    • 接续类(thenXxx)
  • 4. 使用CompletableFuture的一般范式

CompletableFuture是Future的增强版,是多线程开发的利器。本文通俗易懂的介绍了CompletableFuture的用法,最后祭出CompletableFuture的一般使用范式,开箱即用,质量可靠。

1. 什么是CompletableFuture

public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {}
  • Future:代表异步计算的结果
  • CompletionStage:代表异步计算的一个阶段,代表了复杂计算

从以上定义可以看出,CompletableFuture不仅实现了Future接口,还实现了CompletionStage接口,是对Future功能的增强。从Java 8开始引入了CompletableFuture,它针对Future做了改进,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。

2. 为什么需要CompletableFuture

使用Future获得异步执行结果时,要么调用阻塞方法get(),要么轮询看isDone()是否为true,这两种方法都不是很好,因为主线程也会被迫等待。

当需要异步回调、需要复杂计算的支持的时候,CompletableFuture也能大显身手。

3. 使用CompletableFuture

CompletableFuture类的方法非常多,单纯记忆很麻烦,我们需要对它进行分类

创建类

  • supplyAsync:异步执行,有返回值

  • runAsync:异步执行,无返回值

  • anyOf:任意一个执行完成,就可以进行下一步动作

  • allOf:全部完成所有任务,才可以进行下一步任务

// 返回一个0
CompletableFuture.supplyAsync(() -> 0);

接续类(thenXxx)

  • 接续类是CompletableFuture最重要的特性,没有这个的话,CompletableFuture就没意义了,用于注入回调行为。

  • 我们知道Java 8函数式接口有4种常见类型Function、Supplier、Consumer、Runnable,好巧不巧的是CompletableFuture的续传方法也支持这4种类型的接口,一一对应关系列出如下表:

Java 8函数式接口CompletableFuture的接续方法说明
Function(有参数有返回)thenApply方法不使用thenApplyAsync异步
Supplier(无参数有返回)supplyAsync方法,runAsync方法在创建CompletableFuture时使用过了
Consumer(有参数无返回)thenAccept方法不使用thenAcceptAsync异步
Runnable(无参数无返回)thenRun方法不使用thenRunAsync异步
  • 使用thenXxx方法即可,必要使用Async后缀的异步方法。因为后一个步骤依赖前一个步骤的结果
// 以异步计算1+2+3为例
CompletableFuture.supplyAsync(() -> {return 0;
}).thenApply(v -> {try { TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) { e.printStackTrace(); }return v + 1;
}).thenApply(v -> {try { TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) { e.printStackTrace(); }return v + 2;
}).thenApply(v -> {try { TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) { e.printStackTrace(); }return v + 3;
});
System.out.println("main线程可以异步干点别的事情,不用等待计算完成");

4. 使用CompletableFuture的一般范式

一般的,我们使用CompletableFuture就是为了使用多线程异步加快查询速度的,更多的读操作而不是写操作,所以就有必要创建多个CompletableFuture对象。

假设,在微服务架构中,需要一次性返回用户端的订单、收货地址,而这些信息分别在订单微服务、收货地址微服务中,为了快速响应用户,必然是选择开启多个线程同时查询2个微服务最后合并查询结果返回给用户。

一般的,使用以下CompletableFuture的一般范式就可以,复制粘贴开箱即用。

Result result = new Result();
// <1> 查询订单
CompletableFuture<List<Order>> orderCompletableFuture = CompletableFuture.supplyAsync(() -> {// <1.1> http查询订单List<Order> orderList = queryOrderList(uid);// <1.2> 设置结果集result.setOrderList(orderList);return orderList;
})
// <2> 后续处理
.thenApply(v -> {System.out.println("此处可以继续处理...");return v;
});CompletableFuture<List<Address>> addressCompletableFuture = CompletableFuture.supplyAsync(() -> {List<Address> addressList = queryAddressList(uid);result.setAddressList(addressList);return addressList;
});// <3> 等待所有任务执行完成
CompletableFuture.allOf(orderCompletableFuture, addressCompletableFuture);// <4> 记录异常信息并抛出
List<String> errMessage = new ArrayList<>();
List<CompletableFuture<?>> completableFutures = Arrays.asList(whenComplete, whenComplete1);
for (int i = 0; i < completableFutures.size(); i++) {CompletableFuture<?> future = completableFutures.get(i);int finalI = i;future.exceptionally(ex -> {String str = "列表位置索引" + finalI + "处发生异常,异常信息是" + ex.getMessage();errMessage.add(str);throw new RuntimeException("Error occurred: " + ex);});
}// <5> 异常处理
if(CollUtil.isNotEmpty(errMessage)){log.error("计算过程发生异常,异常有{}处,异常详细信息是:{}", errMessage.size(), errMessage);throw new RuntimeException("存在异常,可能有多处请逐个排查,异常信息列表是" + errMessage);
}
// <6> 返回结果给用户
return result;
  • <1>处,使用了CompletableFuture.supplyAsync方法,异步且有返回,这是最常用的方式

    supplyAsync方法是异步有返回值,而runAsync是异步没有返回值,supplyAsync方法更加通用,选择它就完事了

    • <1.1>处,可以通过RestTemplate或Ribbon等等方式从订单微服务查询订单并解析
    • <1.2>处,设置订单信息到结果集中
  • <2>处,可以对上一步的信息做进一步处理。可选的。

    此处可以使用thenApply、thenAccept、thenRun等方法

    因为依赖上一步的结果,所以建议这里都使用thenXxx方法而没有必要使用thenXxxAsync异步方法

  • <3>处,等待订单查询和地址查询都完成

  • <4>处,记录异常日志并重新抛出异常。必要的。

    必须记录日志,如果不记录异常,会导致异常信息丢失

  • <5>处,是否重新抛出异常。可选的。

    有的情况下,个别接口异常我们也是可以接受的,可以不抛出异常,但是必须记录异常。

  • <6>处,给用户返回订单和地址的结果集

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/29535.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue实现文件下载

实现效果图&#xff1a;点击蓝色文字&#xff0c;下载文件 代码实现&#xff1a; <div v-for"(item, index) in form.fileList" :key"index"><i class"el-icon-upload" style"color: #c0c4cc; margin-right: 5px"></i&…

【CSS3】CSS3 动画 ③ ( 动画属性 | CSS3 常见动画属性简介 | 动画属性简写方式 | 动画属性简写语法 | 使用动画制作热点地图 )

文章目录 一、CSS3 动画属性1、CSS3 常见动画属性简介2、代码示例 - CSS3 常见动画属性使用 二、CSS3 动画属性简写方式1、CSS3 动画属性简写语法2、animation 简写动画属性提示3、动画属性简写形式与原形式对比4、代码示例 - CSS3 动画属性简写示例 三、使用动画制作热点地图1…

基于Echarts的大数据可视化模板:智慧物流管理

目录 引言物流管理的重要性大数据可视化在解决物流管理挑战中的作用智慧物流概述定义智慧物流的概念和特点智慧物流的关键技术和平台风险管理和预测:交通拥堵情况和风险预警Echarts与大数据可视化Echarts库以及其在大数据可视化领域的应用优势开发过程和所选设计方案模板如何满…

医疗行业如何防范弱口令攻击?这份弱口令治理方案请收好

随着5G、云计算、物联网等新兴技术与传统医疗系统的不断深化融合&#xff0c;我国医疗信息化程度越来越高&#xff0c;逐步向数字化、智慧化医疗演进&#xff0c;蓬勃发展的信息化也使医疗行业面临的安全风险逐渐增多。数据泄露、勒索病毒等问题频发&#xff0c;加之《等保》、…

微信开发之朋友圈自动点赞的技术实现

简要描述&#xff1a; 朋友圈点赞 请求URL&#xff1a; http://域名地址/snsPraise 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型说明wId…

摄像机终端IP地址白名单配置流程

海康摄像头配置白名单流程 1.登录海康摄像机前端 2.进入配置-系统-安全管理-IP地址过滤 3.IP地址过滤方式选择“允许” 4.点击添加按钮输入对应的IP地址或者IP网段 5.最后勾选启用IP地址过滤&#xff0c;然后保存 大华摄像头配置白名单流程 1.登录大华摄像机前端 2.进入设…

使用MethodInterceptor和ResponseBodyAdvice做分页处理

目录 一、需求 二、代码实现 父pom文件 pom文件 配置文件 手动注册SqlSessionFactory&#xff08;MyBatisConfig &#xff09; 对象 实体类Users 抽象类AbstractQuery 查询参数类UsersQuery 三层架构 UsersController UsersServiceImpl UsersMapper UsersMapper.…

苹果电脑图像元数据编辑器:MetaImage for Mac

MetaImage for Mac是一款功能强大的照片元数据编辑器&#xff0c;它可以帮助用户编辑并管理照片的元数据信息&#xff0c;包括基本信息和扩展信息。用户可以根据需要进行批量处理&#xff0c;方便快捷地管理大量照片。 MetaImage for Mac还提供了多种导入和导出格式&#xff0…

12v转5v降压模块

问&#xff1a;什么是12V转5V降压模块&#xff1f;它的功能是什么&#xff1f; 答&#xff1a;12V转5V降压模块是一种电子设备&#xff0c;用于将输入电压为12V的直流电转换为输出电压为5V的直流电。它的主要功能是为电子设备提供所需的适当电压&#xff0c;以便它们能够正常运…

mysql进阶篇(二)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

[QCM6125][Android13] 默认关闭SELinux权限

文章目录 开发平台基本信息问题描述解决方法 开发平台基本信息 芯片: QCM6125 版本: Android 13 kernel: msm-4.14 问题描述 正常智能硬件设备源码开发&#xff0c;到手的第一件事就是默认关闭SELinux权限&#xff0c;这样能够更加方便于调试功能。 解决方法 --- a/QSSI.1…

GCC编译过程:预处理->编译->汇编->链接

目录 引言 概括介绍 一、预处理 二、编译 三、汇编 四、链接 总结 引言 当使用集成开发环境&#xff08;IDE&#xff09;进行C语言编程时&#xff0c;点击"编译"按钮后&#xff0c;整个C程序从源代码到可执行文件的生成过程会自动完成。IDE会在后台为我们执行C…

QT QLCDNumber 使用详解

本文详细的介绍了QLCDNumber控件的各种操作&#xff0c;例如&#xff1a;新建界面、源文件、设置显示位数、设置进制、设置外观、设置小数点、设置溢出、显示事件、其它文章等等操作。 实际开发中&#xff0c;一个界面上可能包含十几个控件&#xff0c;手动调整它们的位置既费时…

Activity启动过程详解(Android 12源码分析)

Activity的启动方式 启动一个Activity&#xff0c;通常有两种情况&#xff0c;一种是在应用内部启动Activity&#xff0c;另一种是Launcher启动 1、应用内启动 通过startActivity来启动Activity 启动流程&#xff1a; 一、Activity启动的发起 二、Activity的管理——ATMS 三、…

怎么在JMeter中的实现关联

我们一直用的phpwind这个系统做为演示系统, 如果没有配置好的同学, 请快速配置之后接着往下看哦. phpwind发贴时由于随着登陆用户的改变, verifycode是动态变化的, 因此需要用到关联. LoadRunner的关联函数是reg_save_param, Jmeter的关联则是利用后置处理器来完成. 在需要查…

字节编码学习

字节编码学习 文章目录 字节编码学习01_字节与ASCII码表02_每个国家都有独特的码表03_国际化UTF-804_编码本和解码本不一致&#xff0c;乱码 01_字节与ASCII码表 public class Demo01 {public static void main(String[] args) {// 计算机的底层全部都是字节 ---- ----// 一个…

iOS开发-导航栏UINavigationBar隐藏底部线及透明度

iOS 导航栏UINavigationBar隐藏底部线及透明度 苹果官方给出的解释&#xff1a; 如果你不调用方法设置一张背景图片的话&#xff0c;那就给你默认一张&#xff0c;然后同时还有一张阴影图片被默认设置上去&#xff0c;这就是导航栏上1px黑线的由来。 解决办法&#xff1a; 方…

1.利用matlab建立符号表达式(matlab程序)

1.简述 、 1. 使用sym命令创建符号变量和表达式 语法&#xff1a; sym(‘变量’,参数) %把变量定义为符号对象 说明&#xff1a;参数用来设置限定符号变量的数学特性&#xff0c;可以选择为’positive’、’real’和’unreal’&#xff0c; ’positive’ 表示为“正、实”符…

C++的auto究竟是何方神圣

C的auto究竟是何方神圣 前言&#x1f64c;auto&#xff08;C 11&#xff09; 的使用细则auto是什么&#xff1f; auto声明的变量是在什么时期被编译器推导出来呢&#xff1f;为什么使用auto进行定义变量时&#xff0c;必须进行初始化&#xff1f; auto 的使用场景auto与指针和引…

软件安全测试包含哪些内容和方法?安全测试报告的必要性

软件安全测试是一种通过模拟真实攻击的方式&#xff0c;对软件系统进行全面的安全性评估和测试&#xff0c;以发现潜在的安全漏洞和弱点&#xff0c;是确保软件系统安全性的重要措施。在进行软件安全测试时&#xff0c;我们需要了解测试的内容和方法&#xff0c;以及为什么进行…