CompletableFuture是如何提升Future性能的
Future优点
Future优点在于我们可以异步地进行一些非常密集的计算,而不会阻塞当前的线程,这样,我们在此期间就可以做一些其他的工作。
但是,当获取结果的时候,future想要获取结果的时候,会在主线程中阻塞住。同时,考虑下多个 Future的场景。如果我们有多了 Future,而且这些 Future之间产生关系。
-
场景1:第一个 Future 的返回值是第二个 Future 的输入
-
场景2:创建三个 Future,f1需要20s,f2需要5s,f3需要10s,然后我们将他们
list.add(f1);list.add(f2);list.add(f3)
,再依次fx.get()
,你会发现,即使f2先执行完,也要等f1执行完,f2.get
才能返回。
- CompeletionService解决了这个缺点,参见呵呵,面试官问我知不知道CompletionService?
下面罗列了 Future 几个缺点:
Future缺点
-
Future.get方法虽然可以设置超时时间,但是在超时时间到来前无法手动结束或完成
-
Future provides a get() method which blocks until the result is available. further action can not be performed on a Future’s result without blocking the primary application thread Future.get()方法是阻塞的,直到有返回值返回。也就是说在不阻塞主应用程序线程的情况下,无法对 Future 的结果执行进一步的操作
-
Asynchronous workflows can not be created by chaining multiple Futures together. 多个 Future 不能链(chain)在一起来创建异步工作流
-
Futures which are running in parallel, can not be combined together. 并行运行的 Future 不能合并在一起
-
Future API does not have any exception handling construct. Future API 没有异常处理逻辑
举例说明
实例1:stringFuture.get()无法手动停止或完成
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> stringFuture = executor.submit(() -> neverEndingComputation());
System.out.println("The result is: " + stringFuture.get());
如上代码,stringFuture.get()
永远不会有返回值。
实例2: 多个 Future 之间存在依赖关系时
第一个 Future 的返回值是第二个 Future 的输入,代码如下:
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> firstApiCallResult = executor.submit(
() -> firstApiCall(someValue)
);
String firstResult = firstApiCallResult.get(); // 主线程阻塞
Future<String> secondApiCallResult = executor.submit(
() -> secondApiCall(firstResult)
);
如上代码,可以看到,第二个 Future 需要等待第一个 Future的返回值,而且第一个 Future 的返回值是在主线程中阻塞获取的。
CompletableFuture如何解决Future缺点的
实例1的答案
针对实例1
的问题,CompletableFuture 如何解决的呢。CompletableFuture 有个 complete(String)方法,他可以手动结束执行中的任务。回顾下实例1
的代码及 CompletableFuture 的代码
实例1
:
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> stringFuture = executor.submit(() -> neverEndingComputation());
System.out.println("The result is: " + stringFuture.get());
- 解决
实例1
问题的代码
CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> neverEndingComputation());
stringCompletableFuture.complete("Completed");
System.out.println("Is the stringCompletableFuture done ? " + stringCompletableFuture.isDone());
- result: Is the stringCompletableFuture done ? true
查看下 CompletableFuture.complete(arg)的源码注释就明白了
实例2的答案
实例2
的问题是两个有关联的 Future如何能真正做到异步呢。CompletableFuture的链式(chain)方法就是答案。
实例2
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> firstApiCallResult = executor.submit(
() -> firstApiCall(someValue)
);
String firstResult = firstApiCallResult.get(); // 主线程阻塞
Future<String> secondApiCallResult = executor.submit(
() -> secondApiCall(firstResult)
);
- 解决
实例2
问题的代码
var finalResult = CompletableFuture.supplyAsync(
() -> firstApiCall(someValue)
).thenApply(firstApiResult -> secondApiCall(firstApiResult));
可以看到,使用CompletableFuture
链式(chain)方法期间,没有和主线程有任何交互。更进一步,你可以在每个链式(chain)方法中打印下线程名
,你会发现都不是主线程名。也就是说,CompletableFuture
链式(chain)方法完全做到了全程无阻塞。
可以看到,CompletableFuture 与 Java Streams 非常相似。
it`s time to summary
CompletableFuture
解决了 Future
在多个 Future
有关联的场景下的不足。同时,CompletableFuture
也可以主动/手动去结束/完成异步任务。而且,CompletableFuture
提供了非常丰富的方法。
转载自:https://juejin.cn/post/7212101184709115961