ExecutorService与CompletableFuture

Dwa*_*rak 5 java asynchronous executorservice java-8 completable-future

我一直在尝试实现异步过程,其中父方法调用子方法,而子方法又将调用三个不同的方法。我希望所有这些过程都是异步完成的,即在子方法中的这三个调用并行执行后,控件应返回到父方法,并继续执行其其余部分。

我有这段代码,经过测试可以正常工作。

public ReturnSomething parent(){
    child();
    ...//rest to UI
}

private void child(){
  ExecutorService executorService = Executors.newFixedThreadPool(3);

  Runnable service1 = () -> {
     MyFileService.service1();
  };

  Runnable service2 = () -> {
      MyFileService.service2();
  };

  Runnable service3 = () -> {
      MyFileService.service3();
  };

  executorService.submit(service1);
  executorService.submit(service2);
  executorService.submit(service3);
}
Run Code Online (Sandbox Code Playgroud)

现在,我的负责人要我改用它。

public ReturnSomething parent(){
    child();
    ...//rest to UI
}

private void child(){
    CompletableFuture.supplyAsync(() ->  MyFileService.service1();
    CompletableFuture.supplyAsync(() ->  MyFileService.service2();
    CompletableFuture.supplyAsync(() ->  MyFileService.service3();
}
Run Code Online (Sandbox Code Playgroud)

我知道CompletableFuture是Java 8的新功能,但是第二代码比第一代码好吗?因为对于ExecutorService,我没有调用“ get()”方法,所以我不会等待aysnc响应。那么,可以请人解释一下有什么区别吗?

Did*_*r L 11

在功能上,这两种方法或多或少是相同的:

  • 您提交任务以供执行;
  • 你不等结果。

但是,从技术上讲,存在一些细微的差异:

  • 在第二种方法中,您没有指定执行程序,因此它将使用通用的ForkJoinPool. supplyAsync()如果您不想要,则必须将 executor 作为第二个参数传递;
  • CompletableFutureAPI 允许轻松地将更多调用链接到thenApply()thenCompose()等等。因此它比简单的Future返回更灵活ExecutorService.submit()
  • UsingCompletableFuture允许child()使用return CompletableFuture.allOf(the previously created futures).

关于可读性,这是一个偏好问题,但如果您想要等效的代码,CompletableFuture一旦您对其进行了类似的格式化,该方法可能会被认为可读性稍差。相比:

executorService.submit(MyFileService::service1);
executorService.submit(MyFileService::service2);
executorService.submit(MyFileService::service3);
Run Code Online (Sandbox Code Playgroud)

CompletableFuture.supplyAsync(MyFileService::service1, executorService);
CompletableFuture.supplyAsync(MyFileService::service2, executorService);
CompletableFuture.supplyAsync(MyFileService::service3, executorService);
Run Code Online (Sandbox Code Playgroud)

  • 它们是有区别的。`CompletableFuture` 在可能导致意外行为的背后使用了 `ForkJoinPool#commonPool`。请在此处阅读更多信息 https://dzone.com/articles/be-aware-of-forkjoinpoolcommonpool (6认同)

Ale*_*hin 4

在这两种情况下,您都不需要等待结果。

第二种方法的优点就是更少的样板代码。这就是runAsync()supplyAsync()的好处。

但如果你实际上没有返回任何值,你应该使用runAsync()

第二种方法还提供了等待所有 future 的能力CompletableFuture.allOf()。这在第一个场景中也不存在。

  • 它的样板较少,因为它使用常见的“ForkJoinPool”。如果你想使用自己的执行器,它就会变得更加样板。关于等待所有 future 的能力,也可以使用第一种方法来实现:只需对每个 future 调用 `get()` 即可。当您想要链接更多调用或返回它时,“allOf()”更有用。 (2认同)