顺序执行4个商品是4秒,然后又调用了Discount服务又4秒 所以是8秒。 虽然我们现在把流转换为并行流 性能会很好 但是数量大于8时也很慢。相反,使用自定义CompletableFuture执行器能够更充分的利用CPU资源。
List<CompletableFuture<String>> priceFutures = shops.stream()
//异步获取每个shop中的价格
.map(shop -> CompletableFuture.supplyAsync(
() -> shop.getPrice("hhhhh", executor)
))
//Quote对象存在时,对其返回值进行转换
.map(future -> future.thenApply(Quote::parse))
//使用另一个异步任务构造期望的future,申请折扣
.map(future -> future.thenCompose(quote ->
CompletableFuture.supplyAsync(
() -> Discount.applyDiscount(quote), executor)
))
.collect(toList());
//等待流中的所有Future执行完毕,提取各自的返回值
List<String> str = priceFutures.stream().map(CompletableFuture::join).collect(toList());
System.out.print(str);
2126
使用的这三个map跟同步没有太大的区别,但是使用了CompletableFuture类提供的特性,在需要的地方把他们变成了异步操作。
thenApply方法:当第一个Future运行结束,返回CompletableFuture<String>对象转换为CompleTableFuture<Quote>对象。
thenCompose方法:将两个异步操作进行流水线,当第一个操作完成时,将其结果作为参数传递给第二个操作。换句话说,你可以创建两个CompletableFuture对象,对第一个对象调用thenCompose,并向其传递一个函数。
这个方法也有Async版本:thenComposeAsync,通常带后缀的版本是讲任务移交到一个新线程,不带后缀的在当前线程执行。对于这个例子我们没有加上后缀,因为对于最终结果,或者大致的时间而言都没有多少差别,少了很多线程切换的开销。
合并两个CompletableFuture,无论是否依赖
与上面不同,第二个CompletableFuture无需等待第一个CompletableFuture运行结束。而是,将两个完全不相干的CompletableFuture对象整合起来,不希望等到第一个任务完全结束才开始第二个任务。
这种情况应该使用thenCombine方法,它接受名为BiFunction的第二个参数,这个参数定义了当两个CompletableFuture对象完成计算后,结果如何合并。同thenCompose方法一样,thenCombine方法也提供了一个Async的版本。使用thenCombineAsync会导致BiFunction中定义的合并操作被提交到线程池中,由另一个任务以异步的方式执行。
回到这个例子,比如说我们现在需要第三个CompletableFuture来获取汇率,展示美元。当前两个CompletableFuture计算出结果,并由BiFunction方法完全合并后,由它来最终将诶书这一任务:
Future<Double> futurePriceUSD = CompletableFuture.supplyAsync(()->shops.get(0).getPrice("gg"))
.thenCombine(
CompletableFuture.supplyAsync(
()-> 0.66 //远程服务获取 汇率
),(price,rate) -> price * rate
);
这里 第一个参数price 是 getPrice的返回值 double , 第二个参数 rate 是第二个工厂方法返回的0.66 偷了个懒, 最后是他们的结果进行乘法操作 返回最终结果。
响应CompletableFuture的completion事件