Java8实战-总结49

Java8实战-总结49

  • CompletableFuture:组合式异步编程
    • 对多个异步任务进行流水线操作
      • 构造同步和异步操作
      • 将两个 CompletableFuture 对象整合起来,无论它们是否存在依赖

CompletableFuture:组合式异步编程

对多个异步任务进行流水线操作

构造同步和异步操作

使用CompletableFuture提供的特性,以异步方式重新实现findPrices方法。详细代码如下所示(使用CompletableFuture实现findPrices方法):

public List<String> findPrices(String product) { 
	List<CompletableFuture<String>> priceFutures = 
				shops.stream()
					.map(shop -> CompletableFuture.supplyAsync( //以异步方式取得每个shop中指定产品的原始价格
							() -> shop.getPrice(product), executor))
					.map(future -> future.thenApply(Quote::parse)) //Quote对象存在时,对其返回的值进行转换
					.map(future -> future.thenCompose(quote -> //使用另一个异步任务构造期望的Future,申请折扣
							CompletableFuture.supplyAsync(
								() -> Discount.applyDiscount(quote), executor))) 
						.collect(toList()); 
	return priceFutures.stream()
			.map(CompletableFuture::join)//等待流中的所有Future执行完毕,并提取各自的返回值
			.collect(toList()); 
} 

这一次,事情看起来变得更加复杂了这三次转换的流程如下图所示:
在这里插入图片描述
进行的这三次map操作和前面代码中的同步方案没有太大的区别,不过使用CompletableFuture类提供的特性,在需要的地方把它们变成了异步操作。

  • 获取价格
    这三个操作中的第一个已经在各个例子中见过很多次,只需要将Lambda表达式作为参数传递给supplyAsync工厂方法就可以以异步方式对shop进行查询。第一个转换的结果是一个Stream<CompletableFuture<String>>,一旦运行结束,每个CompletableFuture对象中都会包含对应shop返回的字符串。注意,你对CompletableFuture进行了设置,用前面代码中的方法向其传递了一个订制的执行器Executor
  • 解析报价
    现在需要进行第二次转换将字符串转变为订单。由于一般情况下解析操作不涉及任何远程服务,也不会进行任何I/O操作,它几乎可以在第一时间进行,所以能够采用同步操作,不会带来太多的延迟。由于这个原因,你可以对第一步中生成的CompletableFuture对象调用它的thenApply,将一个由字符串转换Quote的方法作为参数传递给它。注意到了吗?直到调用的CompletableFuture执行结束,使用的thenApply方法都不会阻塞代码的执行。这意味着CompletableFuture最终结束运行时,你希望传递Lambda表达式给thenApply方法,将Stream中的每个CompletableFuture<String>对象转换为对应的CompletableFuture<Quote>对象。你可以把这看成是为处理CompletableFuture的结果建立了一个菜单,就像你曾经为Stream的流水线所做的事儿一样。
  • 为计算折扣价格构造Future
    第三个map操作涉及联系远程的Discount服务,为从商店中得到的原始价格申请折扣率。这一转换与前一个转换又不大一样,因为这一转换需要远程执行(或者,就这个例子而言,它需要模拟远程调用带来的延迟),出于这一原因,你也希望它能够异步执行。为了实现这一目标,你像第一个调用传递getPricesupplyAsync那样,将这一操作以Lambda表达式的方式传递给了supplyAsync工厂方法,该方法最终会返回另一个CompletableFuture对象。到目前为止,你已经进行了两次异步操作,用了两个不同的CompletableFutures对象进行建模,你希望能把它们以级联的方式串接起来进行工作。
  • shop对象中获取价格,接着把价格转换为Quote
  • 拿到返回的Quote对象,将其作为参数传递给Discount服务,取得最终的折扣价格。

Java 8CompletableFuture API提供了名为thenCompose的方法,它就是专门为这一目的而设计的,thenCompose方法允许你对两个异步操作进行流水线,第一个操作完成时,将其结果作为参数传递给第二个操作。换句话说,你可以创建两个CompletableFutures对象,对第一个CompletableFuture对象调用 thenCompose,并向其传递一个函数。当第一个CompletableFuture执行完毕后,它的结果将作为该函数的参数,这个函数的返回值是以第一个CompletableFuture的返回做输入计算出的第二个CompletableFuture对象。使用这种方式,即使Future在向不同的商店收集报价,主线程还是能继续执行其他重要的操作,比如响应 事件。
将这三次map操作的返回的Stream元素收集到一个列表,你就得到了一个List<CompletableFuture<String>>,等这些CompletableFuture对象最终执行完毕,就可以像之前代码中那样利用join取得它们的返回值。代码实现的新版findPrices方法产生的输出如下:

[BestPrice price is 110.93, LetsSaveBig price is 135.58, MyFavoriteShop price 
 is 192.72, BuyItAll price is 184.74, ShopEasy price is 167.28] 
Done in 2035 msecs 

上面代码中使用的thenCompose方法像CompletableFuture类中的其他方法一样,也提供了一个以Async后缀结尾的版本thenComposeAsync。通常而言,名称中不带Async的方法和它的前一个任务一样,在同一个线程中运行;而名称以Async结尾的方法会将后续的任务提交到一个线程池,所以每个任务是由不同的线程处理的。就这个例子而言,第二个CompletableFuture对象的结果取决于第一个CompletableFuture,所以无论你使用哪个版本的方法来处理CompletableFuture对象,对于最终的结果,或者大致的时间而言都没有多少差别。选择thenCompose方法的原因是因为它更高效一些,因为少了很多线程切换的开销。

将两个 CompletableFuture 对象整合起来,无论它们是否存在依赖

上面的代码中,你对一个CompletableFuture对象调用了thenCompose方法,并向其传递了第二个 CompletableFuture,而第二个CompletableFuture又需要使用第一个CompletableFuture的执行结果作为输入。但是,另一种比较常见的情况是,你需要将两个完全不相干的CompletableFuture对象的结果整合起来,而且你也不希望等到第一个任务完全结束才开始第二项任务。

这种情况,你应该使用thenCombine方法,它接收名为BiFunction的第二参数,这个参数定义了当两个CompletableFuture对象完成计算后,结果如何合并。同thenCompose方法一样,thenCombine方法也提供有一个Async的版本。这里,如果使用thenCombineAsync会导致BiFunction中定义的合并操作被提交到线程池中,由另一个任务以异步的方式执行。回到我们正在运行的这个例子,有一家商店提供的价格是以欧元(EUR)计价的,但是你希望以美元的方式提供给你的客户。你可以用异步的方式向商店查询指定商品的价格,同时从远程的汇率服务那里查到欧元和美元之间的汇率。当二者都结束时,再将这两个结果结合起来,用返回的商品价格乘以当时的汇率,得到以美元计价的商品价格。用这种方式,你需要使用第三个CompletableFuture 对象,当前两个 CompletableFuture 计算出结果,并由BiFunction方法完成合并后,由它来最终结束这一任务,代码清单如下所示:

Future<Double> futurePriceInUSD = CompletableFuture.supplyAsync(() -> shop.getPrice(product)) //创建第一个任务查询商店取得商品的价格
 									.thenCombine( 
 										CompletableFuture.supplyAsync( 
 											() -> exchangeService.getRate(Money.EUR, Money.USD)), //创建第二个独立任务,查询美元和欧元之间的转换汇率
 										(price, rate) -> price * rate);通过乘法	整合得到的商品价格和汇率
 ); 

这里整合的操作只是简单的乘法操作,用另一个单独的任务对其进行操作有些浪费资源,所以你只要使用thenCombine方法,无需特别求助于异步版本的thenCombineAsync方法。下图展示了上面代码中创建的多个任务是如何在线程池中选择不同的线程执行的,以及它们最终的运行结果又是如何整合的。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计模式—接口隔离原则(ISP)

1.背景 2002 年罗伯特C.马丁给“接口隔离原则”的定义是&#xff1a;客户端不应该被迫依赖于它不使用的方法&#xff08;Clients should not be forced to depend on methods they do not use&#xff09;。该原则还有另外一个定义&#xff1a;一个类对另一个类的依赖应该建立…

【如何修改输入法切换键】

操作 以win10为例&#xff0c;点开系统设置&#xff0c;选择时间和语言 选择语言&#xff0c;找到首选语言中的中文&#xff0c;点击选项 点击其中的微软输入法 选择按键 在按键中进行输入法切换按键的选择

流量分析-PhishingEmail_WriteUp

一、题目问题 问题1&#xff1a;黑客的email名称 问题2&#xff1a;黑客向几人发送了钓鱼邮件 问题3&#xff1a;黑客传输的木马文件名 问题4&#xff1a;下载并运行了木马文件的人的email名称和ip地址&#xff0c;用“-”连接 问题5&#xff1a;黑客用于反弹shell的主机i…

Java游戏制作——王者荣耀

一.准备工作 首先创建一个新的Java项目命名为“王者荣耀”&#xff0c;并在src下创建两个包分别命名为“com.sxt"、”com.stx.beast",在相应的包中创建所需的类。 创建一个名为“img”的文件夹来储存所需的图片素材。 二.代码呈现 package com.sxt;import javax.sw…

大屏可视化编辑器

前言&#xff1a; 乐吾乐Le5le大屏可视化设计器&#xff0c;零代码实现物联网、工业智能制造等领域的可视化大屏、触摸屏端UI以及工控可视化的解决方案。同时也是一个Web组态工具&#xff0c;支持2D、3D等多种形式&#xff0c;用于构建具有实时数据展示、监控预警、丰富交互的组…

【算法】链表-20231127

这里写目录标题 一、面试题 02.02. 返回倒数第 k 个节点二、82. 删除排序链表中的重复元素 II三、141. 环形链表 一、面试题 02.02. 返回倒数第 k 个节点 提示 简单 130 相关企业 实现一种算法&#xff0c;找出单向链表中倒数第 k 个节点。返回该节点的值。 注意&#xff1a;本…

设计模式之十二:复合模式

模式通常被一起使用&#xff0c;并被组合在同一个解决方案中。 复合模式在一个解决方案中结合两个或多个模式&#xff0c;以解决一般或重复发生的问题。 首先重新构建鸭子模拟器&#xff1a; package headfirst.designpatterns.combining.ducks;public interface Quackable …

Java小游戏飞翔的小鸟

游戏界面 运行界面 开发准备 1、eclipse开发工具 二、创建游戏窗口 Mains类作为主类&#xff0c;在mian方法下定义一个m1()方法&#xff0c;设置窗口。 //定义一个初始化的游戏窗口方法 public static void m1() {//获取底层窗口界面的工具类JFrame jf new JFrame();//创建…

The module to import is incompatible with the current project【鸿蒙开发-BUG已解决】

文章目录 项目场景:问题描述原因分析:解决方案:心得体会:知识点OpenHarmony:HarmonyOS:项目场景: 报错: The module to import is incompatible with the current project 问题描述 希望通过 import module 将该模块引入到我的项目。 导入后出现错误,因为项目和模块…

【挑战业余一周拿证】一、亚马逊云科技简介 - 第 1 节 - 模块 1 简介

CSDN 官方中文视频&#xff08;免费&#xff09;&#xff1a;点击进入 一、亚马逊云科技简介 第 1 节 - 模块 1 简介 1、讲师&#xff1a;李锦鸿 部门&#xff1a;亚马逊云科技培训与认证部门 方向&#xff1a;从事数据中心及云计算相关产品与解决方案工作 课程&#xff…

Java 设计模式之命令模式

命令模式 介绍 命令模式是一种行为类设计模式&#xff0c;核心是将每种请求或操作封装为一个独立的对象&#xff0c;从而可以集中管理这些请求或操作&#xff0c;比如将请求队列化依次执行、或者对操作进行记录和撤销。 命令模式通过将请求的发送者&#xff08;客户端&#x…

【批量修改文件名,并去掉括号】

操作 一、 批量修改文件名操作二、去除括号 一、 批量修改文件名操作 在浏览器等下载很多图片后&#xff0c;命名顺序乱七八糟&#xff0c;想要将图片进行重新命名&#xff0c;从数字1开始 首先&#xff0c;全选文件夹中的图片 右键&#xff0c;重明明&#xff0c;选择一张图…

计算机图形学-变换基础

坐标系转换历程模型坐标系 -> 世界坐标系 -> 摄像机坐标系 -> 视口&#xff08;屏幕&#xff09;坐标系 变换 仿射变换和线性变换线性&#xff1a;旋转 缩放 镜像 切变放射&#xff1a; 平移 平移 2D变换矩阵 3D变换矩阵 旋转 2D旋转矩阵 //2D 旋转private (float,…

sql语句在字段中使用select

有两个表如下&#xff1b;产品表&#xff0c;产品评论表&#xff1b; 查询全部产品信息和每种产品的评论数量&#xff1b; 这也是子查询的一种&#xff1b; select * from product1; select * from comment; SELECT product1.*,(select count(id) from comment where product1…

【docker系列】docker命令篇

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

RabbitMQ之消费者可靠性

文章目录 前言一、消费者确认机制二、失败重试机制三、失败处理策略四、业务幂等性唯一消息ID业务判断 五、兜底方案总结 前言 当RabbitMQ向消费者投递消息以后&#xff0c;需要知道消费者的处理状态如何。因为消息投递给消费者并不代表就一定被正确消费了&#xff0c;可能出现…

【Linux】bash 终端指令

进程 $ ps aux | grep pwd work 63317 0.0 0.0 51192 612 pts/9 S 14:22 0:00 grep /home/work/search/1000000.dyenv-user-diaoyan-baiseCliPlus-baisePlus-195522.diaoyan.yq/ala-ac/output_root端口 查看本机端口开放情况 netstat -tln | grep :31 tcp …

122.买卖股票的最佳时机II(不限次数)

题目 题解 labuladong的状态图解 class Solution:def maxProfit(self, prices: List[int]) -> int:N len(prices)# 定义状态&#xff1a;dp[i][j]表示在第i天持有或卖出时的最大利润&#xff0c;j1代表持有&#xff0c;j0代表卖出dp [[0 for j in range(2)] for i in ra…

软件测试面试必杀篇:【2023软件测试面试八股文宝典】

800道软件测试面试真题&#xff0c;高清打印版打包带走&#xff0c;横扫软件测试面试高频问题&#xff0c;涵盖测试理论、Linux、MySQL、Web测试、接口测试、App测试、Python、Selenium、性能测试、LordRunner、计算机网络、数据结构与算法、逻辑思维、人力资源等模块面试题&am…

P16 C++构造函数

目录 前言 01 什么是构造函数呢&#xff1f; 02 非构造函数初始化变量 03 构造函数初始化变量 04 带参数的构造函数。 最后的话 前言 我们继续学习 C 的面向对象编程&#xff0c;本章主要是讲其中的 构造函数。 01 什么是构造函数呢&#xff1f; 构造函数基本上是一种特…