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

【Java】《Java8 实战》 CompletableFuture 学习

文章目录

    • 前言
    • 1. 并发(Concurrent) 和 并行(Parallel)
      • 1.1 并发的来源
      • 1.2 并发技术解决了什么问题
    • 2. 并行的来源
      • 2.1 并行解决了什么问题
    • 3. CompletableFuture 简介
    • 4. CompletableFuture 简单应用
    • 5. CompletableFuture 工厂方法的应用
    • 6. CompletableFuture join() 方法
    • 7. 使用 ParallelStream 还是 CompletableFuture
    • 8. 使用 CompletableFuture 编排异步任务
    • 9. CompletableFuture 响应 completion 事件
    • 10. CompletableFuture 异常处理

前言

看 《Java8 实战》后,觉得自己对多线程应用还是停留在 JUC 工具类的使用上,忽略了 CompletableFuture 这么强大的工具。本文主要内容

  • 复习并行、并发的概念。
  • 多线程的编程模型
  • CompletableFuture 让多线程编程更加清爽
  • 有时间的话,补充 CompletableFuture 的内部原理

1. 并发(Concurrent) 和 并行(Parallel)

Concurrent 和 Parallel 作为形容词,并列到一起。对应Java 的类名/方法名 也有所体现:

  • ConcurrentHashMap
  • parallelStream()

在这里插入图片描述

1.1 并发的来源

在单核CPU的时代,根本不可能真正同时运行一个以上的线程(进程是线程的容器,Linux是把时间片分给线程)。
假设有网易音乐、Chrome浏览器这两个应用需要同时运行,操作系统会 轮流 把这两个应用的任务放到同一个线程上执行。
宏观上看,CPU把时间片分给了不同应用,不同应用持有单个线程某一段时间的运行权力。这就是并发技术。

1.2 并发技术解决了什么问题

在 web 技术中,同一时刻请求的接收能力提高了,具体的:
如果有耗时较长的数据库查询、外部资源请求,一个线程不具有并发能力则耗时操作会一直阻塞后面的请求。

2. 并行的来源

多核CPU的出现

2.1 并行解决了什么问题

除了压榨硬件资源从而提高响应速度外,还尽可能减少任务之间的并发度。因为一个CPU核心管一个任务的情况下,任务之间是隔离的,也就是线程安全的。

3. CompletableFuture 简介

这个类是 Java 8 引入的,用于解决 Futrue 异步编程的局限性:

  • Futrue 任务之间的依赖关系很难表达
  • 等待Futrue集合中的所有任务都完成
  • 应对Future的完成事件

“可以说 CompletableFuture 和 Future 的关系就跟 Stream 和 Collections的关系一样”

4. CompletableFuture 简单应用

  • 定义一个异步任务
public Future<Double> getPriceAsync(String product) {// 用于接收异步任务的响应CompletableFuture<Double> futurePrice = new CompletableFuture<>();// 异步任务new Thread( () -> {try {double price = calculatePrice(product);// 异步任务完成后通知(带上返回值)futurePrice.complete(price);} catch (Exception ex) {// 异步任务有异常,也会通知调用方futurePrice.completeExceptionally(ex);}}).start();return futurePrice;
}
  • 调用异步任务
Future<Double> futurePrice = shop.getPriceAsync("my favorite product");doSomething();try {double pricie = futurePrice.get();
} catch (Exception e) {throw new RuntimeException(e);
}

5. CompletableFuture 工厂方法的应用

  • getPriceAsync 可以用已有的api改写为:
// 同样获得了异步处理、异常处理的能力
public Future<Double> getPriceAsync(String product) {return CompletableFuture.supplyAsync(() -> calculatePrice(product));
}

6. CompletableFuture join() 方法

  • 书中用了两个Stream,因为Stream有延时特性,写在一起的话第一个任务提交后,会被立即join();
  • 立即join的副作用就是,主线程会阻塞等待第一个任务完成后才继续后面操作
  • 进而所有线程都变成了顺序执行
  • 所以需要拆成两个Stream

在这里插入图片描述

// 获取并行运算的任务列表
List<CompletableFuture<String>> priceFutures =shops.stream().map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product)).collect(toList()); // import 了 Collectors.toList()// 汇总并行运算的计算结果
List<String> result = priceFutures.stream().map(CompletableFuture::join).collect(toList());

7. 使用 ParallelStream 还是 CompletableFuture

  • 计算密集型使用 parallelStream() , 其默认的最大并行数就是 CPU核心数,不用额外维护其他参数
  • IO密集或者等待时间不稳定的,使用 CompletableFuture

8. 使用 CompletableFuture 编排异步任务

  • 有依赖关系
List<CompletableFuture<String>> priceFutures =shops.stream()// 获取价格 (异步).map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product))// 解析报价.map(future -> future.thenApply(Quota::parse))// 为计算折扣价构造 future (异步) 【该异步任务需要等待报告被解析出来】.map(future -> future.thenCompose(quota ->CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor)).collect(toList());

在这里插入图片描述

getPrice 和 applyDiscount 都是非阻塞调用,会比阻塞调用快一点

  • 无依赖关系
Future<Double> futurePriceInUSD =shops.stream()// 获取价格 (异步).map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product))// 获取汇率.thenCombine(CompletableFuture.supplyAsync(() -> exchangeService.getRate(Money.EUR, Money.USD)),// 两个异步任务整合, 哪个值先获取到无所谓(price, rate) -> price * rate);

在这里插入图片描述

9. CompletableFuture 响应 completion 事件

CompletableFuture[] futures =shops.stream().map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product)).map(future -> future.thenApply(Quota::parse)).map(future -> future.thenCompose(quota ->CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor))// 【定义事件完成后做什么事】.map(f -> thenAccept(System.out.println)).toArray(size -> new CompletableFuture[size]);// 等待所有子线程执行完成
CompletableFuture.allOf(futures).join();

10. CompletableFuture 异常处理

  • 引用最早的一个代码
public Future<Double> getPriceAsync(String product) {// 用于接收异步任务的响应CompletableFuture<Double> futurePrice = new CompletableFuture<>();// 异步任务new Thread( () -> {try {double price = calculatePrice(product);// 异步任务完成后通知(带上返回值)futurePrice.complete(price);} catch (Exception ex) {// 异步任务有异常,也会通知调用方futurePrice.completeExceptionally(ex);}}).start();return futurePrice;
}

如果 calculatePrice 抛出异常,即 futurePrice.completeExceptionally(ex) 后,futurePrice 的调用端也会抛出运行时异常。这个异常处理也会封装在 CompletableFuture.supplyAsync(() -> calculatePrice(product)); 的api中

  • exceptionally

参考这篇文章

相关文章:

【Java】《Java8 实战》 CompletableFuture 学习

文章目录前言1. 并发(Concurrent) 和 并行(Parallel)1.1 并发的来源1.2 并发技术解决了什么问题2. 并行的来源2.1 并行解决了什么问题3. CompletableFuture 简介4. CompletableFuture 简单应用5. CompletableFuture 工厂方法的应用6. CompletableFuture join() 方法7. 使用 Par…...

Vue3之条件渲染

1.何为条件渲染 条件渲染就是在指定的条件下&#xff0c;渲染出指定的UI。比如当我们显示主页的时候&#xff0c;应该隐藏掉登录等一系列不相干的UI元素。即UI元素只在特定条件下进行显示。而在VUE3中&#xff0c;这种UI元素的显示和隐藏可以通过两个关键字&#xff0c;v-if 和…...

将Nginx 核心知识点扒了个底朝天(四)

为什么 Nginx 不使用多线程&#xff1f; Apache: 创建多个进程或线程&#xff0c;而每个进程或线程都会为其分配 cpu 和内存&#xff08;线程要比进程小的多&#xff0c;所以 worker 支持比 perfork 高的并发&#xff09;&#xff0c;并发过大会榨干服务器资源。 Nginx: 采用…...

设计模式之工厂模式

文章の目录一、什么是工厂模式二、工厂模式有什么用&#xff1f;三、应用场景四、示例1、用字面量的方式创建对象2、使用工厂模式创建对象参考写在最后一、什么是工厂模式 工厂模式是一种众所周知的设计模式&#xff0c;广泛应用于软件工程领域&#xff0c;用于抽象创建特定对…...

80.链表-由来

链表是怎么发展来的 线性表&#xff1a;是n个具有相同特性的数据元素的有限序列。 链表&#xff1a;具有线性存储结构的线性表。 为什么需要使用链表&#xff1f;&#xff08;链表是如何被设计出来的&#xff09; 程序开发最重要的部分是如何在项目程序中找到一种合适的、好…...

元胞自动机

文章目录前言文献阅读摘要主要贡献方法框架实验结论元胞自动机元胞自动机是什么&#xff1f;构成及规则案例及代码实现总结前言 This week,the paper proposes a Multi-directional Temporal Convolutional Artificial Neural Network (MTCAN) model to impute and forecast P…...

设计模式之各种设计模式总结与对比

目录1 目标2 定位3 一句话归纳设计原则4 G0F 23种设计模式简介5 设计模式使用频次总结6 —句话归纳设计模式7 设计模式之间的关联关系和对比1 目标 1、 简要分析GoF 23种设计模式和设计原则,做整体认知。 2、 剖析Spirng的编程思想&#xff0c;启发思维,为之后深入学习Spring…...

JAVA练习55- Fizz Buzz

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、题目-Fizz Buzz 1.题目描述 2.思路与代码 2.1 思路 2.2 代码 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 2月19日练习…...

LeetCode笔记:Biweekly Contest 98

LeetCode笔记&#xff1a;Biweekly Contest 98 1. 题目一 1. 解题思路2. 代码实现 2. 题目二 1. 解题思路2. 代码实现 3. 题目三 1. 解题思路2. 代码实现 4. 题目四 比赛链接&#xff1a;https://leetcode.com/contest/biweekly-contest-98 1. 题目一 给出题目一的试题链接如…...

HNUCM-《算法分析与设计》期末考试考前复习题

问题 A: X星人的地盘题目描述一天&#xff0c;X星人和Y星人在一张矩形地图上玩抢地盘的游戏。X星人每抢到一块地&#xff0c;在地图对应的位置标记一个“X”&#xff1b;Y星人每抢到一块地&#xff0c;在地图对应的位置标记一个“Y”&#xff1b;如果某一块地无法确定其归属则标…...

算法导论【分治思想】—大数乘法、矩阵相乘、残缺棋盘

这里写自定义目录标题分治法概述特点大数相乘问题分治算法矩阵相乘分治算法残缺棋盘分治算法分治法概述 在分而治之的方法中&#xff0c;一个问题被划分为较小的问题&#xff0c;然后较小的问题被独立地解决&#xff0c;最后较小问题的解决方案被组合成一个大问题的解决。 通常…...

Java【七大排序】算法详细图解,一篇文章吃透

文章目录一、排序相关概念二、七大排序1&#xff0c;直接插入排序2&#xff0c;希尔排序3&#xff0c;选择排序4&#xff0c;堆排序5&#xff0c;冒泡排序5.1冒泡排序的优化6&#xff0c;快速排序6.1 快速排序的优化7&#xff0c;归并排序三、排序算法总体分析对比总结提示&…...

Autosar OS IOC

Inter-OS-Application Communicator 背景和基本原理General purposeIOC functionalityCommunicationNotificationIOC interface背景和基本原理 The IOC implementation shall be part of the Operating System IOC和操作系统紧密相关&#xff0c;是操作系统实现的一部分 The IO…...

记录一次Binder内存相关的问题导致APP被杀的BUG排查过程

事情的起因的QA压测过程发生进程号变更&#xff0c;怀疑APP被杀掉过&#xff0c;于是开始看日志 APP的压测平台会上报进程号变更时间点&#xff0c;发现是在临晨12&#xff1a;20分&#xff0c;先大概确定在哪个日志文件去找关键信息一开始怀疑是crash&#xff0c;然后就在日志…...

设计模式(十)----结构型模式之适配器模式

1、概述 如果去欧洲国家去旅游的话&#xff0c;他们的插座如下图最左边&#xff0c;是欧洲标准。而我们使用的插头如下图最右边的。因此我们的笔记本电脑&#xff0c;手机在当地不能直接充电。所以就需要一个插座转换器&#xff0c;转换器第1面插入当地的插座&#xff0c;第2面…...

【数据结构】——队列

文章目录前言一.什么是队列&#xff0c;队列的特点二、队列相关操作队列的相关操作声明队列的创建1.队列的初始化2.对队列进行销毁3.判断队列是否为空队列4.入队操作5.出队操作6.取出队头数据7. 取出队尾数据8.计算队伍的人数总结前言 本文章讲述的是数据结构的特殊线性表——…...

Android OTA升级常见问题的解决方法

1.1 多服务器编译 OTA 报错 Android7 以后引入了 jack-server 功能&#xff0c;也导致在公共服务器上 编译 Android7 以上的版本时&#xff0c;会出现 j ack-server 报错问题。 在多用户服务器上 编译 dist 时 会出现编译过程中 会将 port_service 和 port_admin 改为 默认的 …...

说说Hibernate

当你在实战项目中需要用到SSH时, 如果你之前只用过Mybatis那自然是不能解决问题的, 因为在很多银行类金融类项目中你可能会使用到Hibernate, 那么关于Hibernate你应该要了解什么呢, 本篇文章就以学习Hibernate框架为目的, 巩固在工作中可能需要用到的这种ORM技术, 同时也欢迎家…...

目标检测论文阅读:DETR算法笔记

标题&#xff1a;End-to-End Object Detection with Transformers 会议&#xff1a;ECCV2020 论文地址&#xff1a;https://link.springer.com/10.1007/978-3-030-58452-8_13 官方代码&#xff1a;https://github.com/facebookresearch/detr 作者单位&#xff1a;巴黎第九大学、…...

Golang sync.Once 源码浅析

本文分析了Golang sync.Once 源码&#xff0c;并由此引申&#xff0c;简单讨论了单例模式的实现、 atomic 包的作用和 Java volatile 的使用。 sync.Once 使用例子 sync.Once 用于保证一个函数只被调用一次。它可以用于实现单例模式。 有如下类型&#xff1a; type instanc…...

计算机组成原理核心剖析:CPU、存储、I/O 与总线系统全解

引言 在当今数字化时代&#xff0c;计算机已经渗透到我们生活的方方面面&#xff0c;从智能手机到超级计算机&#xff0c;从智能家居到自动驾驶汽车。然而&#xff0c;你是否曾好奇过&#xff0c;这些功能强大的设备内部究竟是如何工作的&#xff1f;是什么让计算机能够执行各种…...

qwen 2.5 并行计算机制:依靠 PyTorch 和 Transformers 库的分布式能力

qwen 2.5 并行计算机制:依靠 PyTorch 和 Transformers 库的分布式能力 完整可运行代码: import torch import torch.nn.functional as F from transformers...

用dayjs解析时间戳,我被提了bug

引言 前几天开发中突然接到测试提的一个 Bug&#xff0c;说我的时间组件显示异常。 我很诧异&#xff0c;这里初始化数据是后端返回的&#xff0c;我什么也没改&#xff0c;这bug提给我干啥。我去问后端&#xff1a;“这数据是不是有问题&#xff1f;”。后端答&#xff1a;“…...

基于springboot的运动员健康管理系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…...

600+纯CSS加载动画一键获取指南

CSS-Loaders.com 完整使用指南&#xff1a;600纯CSS加载动画库 &#x1f3af; 什么是 CSS-Loaders.com&#xff1f; CSS-Loaders.com 是一个专门提供纯CSS加载动画的资源网站&#xff0c;拥有超过600个精美的单元素加载器。这个网站的最大特色是所有动画都只需要一个HTML元素…...

useMemo useCallback 自定义hook

useMemo & useCallback & 自定义hook useMemo 仅当依赖项发生变化的时候&#xff0c;才去重新计算&#xff1b;其他状态变化时则不去做不必要的计算。 useCallback 缓存函数。但是使用注意&#x1f4e2; &#xff0c;useCallback没有特别明显的优化。 *合适的场景——父…...

AI入门——AI大模型、深度学习、机器学习总结

以下是对AI深度学习、机器学习相关核心技术的总结与拓展&#xff0c;结合技术演进逻辑与前沿趋势&#xff0c;以全新视角呈现关键知识点 一、深度学习&#xff1a;从感知到认知的技术革命 核心突破&#xff1a;自动化特征工程的范式变革 深度学习通过多层神经网络架构&#x…...

docker、ctr、crictl命令简介与使用

概述 在使用k3s过程中&#xff0c;经常需要使用ctr和crictl两个命令&#xff0c;本文记录一下。 ctr 类似docker命令是docker-shim容器运行时的客户端工具&#xff0c;ctr是Containerd的客户端工具。一个简单的CLI接口&#xff0c;用作Containerd本身的一些调试用途&#xf…...

WEB安全--SQL注入--bypass技巧2

继之前文章的补充&#xff1a; WEB安全--SQL注入--bypass技巧_sql注入过滤空格-CSDN博客 Q1&#xff1a;发现sql注入的时间盲注时&#xff0c;如果时间盲注的函数都被过滤了&#xff0c;怎么办&#xff1f; 除了找其他函数替换、编码等方式&#xff0c;还有以下方式绕过&…...

WEB3——什么是ABI

怎么获得ABI&#xff1f; 在编译完合约后&#xff0c;可以在左边下面点击复制ABI ABI&#xff08;Application Binary Interface&#xff0c;应用二进制接口&#xff09;是用来让前端或服务端 JavaScript 代码与智能合约进行交互的桥梁&#xff0c;它描述了合约的函数、事件和…...