Future优点

Future优点在于咱们能够异步地进行一些十分密布的计算,而不会堵塞当前的线程,这样,咱们在此期间就能够做一些其他的作业。

但是,当获取成果的时分,future想要获取成果的时分,会在主线程中堵塞住。一起,考虑下多个 Future的场景。如果咱们有多了 Future,并且这些 Future之间产生联系。

  1. 场景1:第一个 Future 的回来值是第二个 Future 的输入

  2. 场景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缺陷

  1. Future.get办法尽管能够设置超时时刻,但是在超时时刻到来前无法手动完毕或完结

  2. 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 的成果执行进一步的操作

  3. Asynchronous workflows can not be created by chaining multiple Futures together. 多个 Future 不能链(chain)在一起来创立异步作业流

  4. Futures which are running in parallel, can not be combined together. 并行运转的 Future 不能合并在一起

  5. 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性能的

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供给了十分丰富的办法。

参考:java-completablefuture