CompletableFuture
##CompletableFuture 异步组合式编程
###并行处理的目的
在同一个CPU上执行几个松耦合的任务,充分利用CPU的核,让其足够忙碌,从而最大化程序的吞吐量,主要是避免因为等待远程服务的返回,或者对数据库的查询,而阻塞线程的执行, 浪费宝贵的计算资源
并发与并行区别
##Future
可以将那些潜在耗时的操作的线程解放出来,好让程序继续执行有价值的工作,不再需要等待耗时的操作完成
示意图
##Future
局限性,无法满足跟多样化的编程需求(单次调用中有使用多个Future,并相互有关联):
- 等待Future集合中的所有任务都完成。 (比如计算结果合并)
-
仅等待Future集合中最快结束的任务完成(有可能因为它们试图通过不同的方式计算同 (比如同一函数中两种计算价格的方式,谁先计算出来,谁先放回)
一个值),并返回它的结果。
- 通过编程方式完成一个Future任务的执行(即以手工设定异步操作结果的方式)
- 应对Future的完成事件(即当Future的完成事件发生时会收到通知,并能使用Future
##CompletableFuture
常规使用:
结果:
##CompletableFuture 错误接收
使用 CompletableFuture的completeExceptionally方法将导致
CompletableFuture<Double> futurePrice = new CompletableFuture<>(); new Thread( () -> {
try {
double price = calculatePrice(product);
int a = 1/0;
futurePrice.complete(price);
} catch (Exception e) {
futurePrice.completeExceptionally(e);
}
}).start();
CompletableFuture内发生问 题的异常抛出
不加futurePrice.completeExceptionally(e);
加了之后
引申:lambada 表达式中 parallelStream的使用
试看两端代码
List<CompletableFuture<String>> priceFutures =
shops.stream()
.map(shop -> CompletableFuture.supplyAsync(
() -> String.format("%s price is %.2f",
shop.getName(), shop.getPrice(product))))
.collect(toList());
public List<String> findPrices(String product) {
List<CompletableFuture<String>> priceFutures =
shops.stream()
.map(shop -> CompletableFuture.supplyAsync(
() -> shop.getName() + " price is " +shop.getPrice(product)))
.collect(Collectors.toList());
return priceFutures.stream().map(CompletableFuture::join)
.collect(toList());
lambada的会顺序执行一个个拿到结果,而第二种写法在使用单独线程池且数量较多的处理数据量较大的情况下,性能更佳
##并行流与CompletableFuture的选择
- 当计算密集,并且没有IO 推荐选择Stream
- 当并行工作 设计IO, 远程调用,数据库操作等,可以使用CompletableFuture
##异步任务的流水线操作
###thenApply
接收上一个CompletableFuture,继续异步执行操作,返回一个CompletableFuture
###thenCompose
1.thenCompose方法允许你对两个异步操作进行流水线,第一个操作完成时,将其 结果作为参数传递给第二个操作
2.接收上一步骤的结果集,然后异步生成另外CompletableFuture
3.可以创建两个CompletableFutures对象,对 第一个CompletableFuture对象调用thenCompose,并向其传递一个函数。当第一个 CompletableFuture执行完毕后,它的结果将作为该函数的参数,这个函数的返回值是以第一 个CompletableFuture的返回做输入计算出的第二个CompletableFuture对象
注意这里使用这个需要等待上一个CompletableFuture的返回值
###thenCombine
它与thenCompose的区别在于,它可以将两个不相干的CompletableFuture,各自执行,然后将其两的结果进行处理
###thenCombineAsync
与thenCompose类似,但是不在同一个线程中
thenCompose比thenCombineAsync更高效一些,因为少了很多线程切换的开销
Future<Double> futurePriceInUSD =
CompletableFuture.supplyAsync(() -> shop.getPrice(product)) .thenCombine(
CompletableFuture.supplyAsync(
() -> exchangeService.getRate(Money.EUR, Money.USD)),
(price, rate) -> price * rate
);
###CompletableFuture.allOf
多个 CompletableFuture 的协调,等待CompletableFuture集合完成 一起返回
###CompletableFuture.anyOf
多个 CompletableFuture 的协调,CompletableFuture集合中有一个完成 就返回
里氏代换原则:里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现
参考资料:
java 8 实战