Java8实战-总结48
- CompletableFuture:组合式异步编程
- 对多个异步任务进行流水线操作
- 实现折扣服务
- 使用 Discount 服务
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; }
}
通过传递shop
对象返回的字符串给静态工厂方法parse
,可以得到Quote
类的一个实例,它包含了shop
的名称、折扣之前的价格,以及折扣代码。Discount
服务还提供了一个applyDiscount
方法,它接收一个Quote
对象,返回一个字符串,表示生成该Quote
的shop
中的折扣价格,代码如下所示。
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);}
}
使用 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());
}
通过在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
毫无意外,这次执行耗时10秒,因为顺序查询5个商店耗时大约5秒,现在又加上了Discount
服务为5个商店返回的价格申请折扣所消耗的5秒钟。你已经知道,把流转换为并行流的方式,非常容易提升该程序的性能。不过,这一方案在商店的数目增加时,扩展性不好,因为Stream
底层依赖的是线程数量固定的通用线程池。相反,如果自定义CompletableFutures
调度任务执行的执行器能够更充分地利用CPU资源。