• Java8实战-总结48


    CompletableFuture:组合式异步编程

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

    实现折扣服务

    你的“最佳价格查询器”应用现在能从不同的商店取得商品价格,解析结果字符串,针对每个字符串,查询折扣服务取的折扣代码。这个流程决定了请求商品的最终折扣价格(每个折扣代码的实际折扣比率有可能发生变化,所以你每次都需要查询折扣服务)。我们已经将对商店返回字符串的解析操作封装到了下面的Quote类之中:

    public class Quote { 
    
    	private final String shopName; 
    	private final double price; 
    	private final Discount.Code discountCode; 
    	
    	public Quote(String shopName, double price, Discount.Code code) { 
    		this.shopName = shopName;
    		this.price = price; 
    		this.discountCode = code; 
    	} 
    	public static Quote parse(String s) { 
    	 	String[] split = s.split(":"); 
    	 	String shopName = split[0];
    	 	double price = Double.parseDouble(split[1]);
    	 	Discount.Code discountCode = Discount.Code.valueOf(split[2]); 
    	 		return new Quote(shopName, price, discountCode); 
    	} 
    	
    	public String getShopName() { return shopName; } 
    	public double getPrice() { return price; } 
    	public Discount.Code getDiscountCode() { return discountCode; } 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    通过传递shop对象返回的字符串给静态工厂方法parse,可以得到Quote类的一个实例,它包含了shop的名称、折扣之前的价格,以及折扣代码。Discount服务还提供了一个applyDiscount方法,它接收一个Quote对象,返回一个字符串,表示生成该Quoteshop中的折扣价格,代码如下所示。

    public class Discount { 
    	public enum Code { 
     		// 源码暂时省略……
    	} 
    	
    	public static String applyDiscount(Quote quote) {
    		return quote.getShopName() + " price is " + 
    				Discount.apply(quote.getPrice(), 
    							   quote.getDiscountCode()); 
    	} 
    	
    	private static double apply(double price, Code code) { 
    		delay(); 
    		return format(price * (100 - code.percentage) / 100);
    	} 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    使用 Discount 服务

    由于Discount服务是一种远程服务,你还需要增加1秒钟的模拟延迟,代码如下所示。首先尝试以最直接的方式(坏消息是,这种方式是顺序而且同步执行的)重新实现findPrices,以满足这些新增的需求。
    代码清单11-15 以最简单的方式实现使用Discount服务的findPrices方法

    public List<String> findPrices(String product) {
    	return shops.stream()
    				.map(shop -> shop.getPrice(product)) //取得每个shop对象中商品的原始价格
    				.map(Quote::parse) //在Quote对象中对shop返回的字符串进行转换
    				.map(Discount::applyDiscount) //联系Discount服务,为每个Quote申请折扣
    				.collect(toList()); 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    通过在shop构成的流上采用流水线方式执行三次map操作,我们得到了期望的结果。

    • 第一个操作将每个shop对象转换成了一个字符串,该字符串包含了该 shop中指定商品的价格和折扣代码。
    • 第二个操作对这些字符串进行了解析,在Quote对象中对它们进行转换。
    • 最终,第三个map会操作联系远程的Discount服务,计算出最终的折扣价格,并返回该价格及提供该价格商品的shop

    这种实现方式的性能远非最优,不过还是应该测量一下。跟之前一样,通过运行基准测试,得到下面的数据:

    [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 10028 msecs 
    
    • 1
    • 2
    • 3

    毫无意外,这次执行耗时10秒,因为顺序查询5个商店耗时大约5秒,现在又加上了Discount服务为5个商店返回的价格申请折扣所消耗的5秒钟。你已经知道,把流转换为并行流的方式,非常容易提升该程序的性能。不过,这一方案在商店的数目增加时,扩展性不好,因为Stream底层依赖的是线程数量固定的通用线程池。相反,如果自定义CompletableFutures调度任务执行的执行器能够更充分地利用CPU资源。

  • 相关阅读:
    【计算机网络】Linux 内核网络概述
    【Docker】Docker的工具实践及root概念,时间戳的概念和Docker容器安全性设置的详细讲解
    NAO机器人程序设计——接力赛准备
    微信小程序的tabbar怎么配置
    简述《华为数据之道》
    嵌入式系统开发笔记95:安装STM32CubeIDE
    ChatGPT做测试助手,轻轻松提升工作效率!
    java替换jar中的class文件
    PMP考试提分必刷题
    Java并发系列:什么是锁
  • 原文地址:https://blog.csdn.net/weixin_42583701/article/details/134498150