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()无法手动停止或完成
1 | ExecutorService executor = Executors.newSingleThreadExecutor(); |
如上代码,stringFuture.get()
永远不会有返回值。
实例2: 多个 Future 之间存在依赖关系时
第一个 Future 的返回值是第二个 Future 的输入,代码如下:1
2
3
4
5
6
7
8
9
10ExecutorService 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
:1
2
3ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> stringFuture = executor.submit(() -> neverEndingComputation());
System.out.println("The result is: " + stringFuture.get());解决
实例1
问题的代码1
2
3CompletableFuture<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
1
2
3
4
5
6
7
8
9
10ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> firstApiCallResult = executor.submit(
() -> firstApiCall(someValue)
);
String firstResult = firstApiCallResult.get(); // 主线程阻塞
Future<String> secondApiCallResult = executor.submit(
() -> secondApiCall(firstResult)
);解决
实例2
问题的代码
1 | var finalResult = CompletableFuture.supplyAsync( |
可以看到,使用CompletableFuture
链式(chain)方法期间,没有和主线程有任何交互。更进一步,你可以在每个链式(chain)方法中打印下线程名
,你会发现都不是主线程名。也就是说,CompletableFuture
链式(chain)方法完全做到了全程无阻塞。
可以看到,CompletableFuture 与 Java Streams 非常相似。
it`s time to summary
CompletableFuture
解决了 Future
在多个 Future
有关联的场景下的不足。同时,CompletableFuture
也可以主动/手动去结束/完成异步任务。而且,CompletableFuture
提供了非常丰富的方法。