Java CompletableFuture的thenApply和thenApplyAsync有什么区别?

时间:2017-11-25 18:37:45

标签: java completable-future

假设我有以下代码:

CompletableFuture<Integer> future  
        = CompletableFuture.supplyAsync( () -> 0);

thenApply案例:

future.thenApply( x -> x + 1 )
      .thenApply( x -> x + 1 )
      .thenAccept( x -> System.out.println(x));

此处输出为2.现在为thenApplyAsync

future.thenApplyAsync( x -> x + 1 )   // first step
      .thenApplyAsync( x -> x + 1 )   // second step
      .thenAccept( x -> System.out.println(x)); // third step

我在此blog中读到,每个thenApplyAsync都是在一个单独的帖子中执行的,同时&#39;(这意味着在thenApplyAsyncs之前开始{之前} { {1}}完成),如果是这样,如果第一步没有完成,第二步的输入参数值是多少?

如果第二步不采取第一步的结果,将在何处进行? 第三步将采取哪一步结果?

如果第二步必须等待第一步的结果,那么thenApplyAsyncs的重点是什么?

这里x - &gt; x + 1只是为了表明这一点,我想知道的是计算时间非常长。

3 个答案:

答案 0 :(得分:19)

与负责运行代码的Executor有所不同。 CompletableFuture上的每个运营商通常有3个版本。

  1. thenApply(fn) - 在调用它的fn定义的线程上运行CompleteableFuture,因此您通常无法知道它将在何处执行。如果结果已经可用,它可能会立即执行。
  2. thenApplyAsync(fn) - 在环境定义的执行程序上运行fn,无论情况如何。对于CompletableFuture,这通常为ForkJoinPool.commonPool()
  3. thenApplyAsync(fn,exec) - 在fn上运行exec
  4. 最后结果是一样的,但调度行为取决于方法的选择。

答案 1 :(得分:1)

我必须指出,thenApplythenApplyAsync这两个名字绝对是令人恐惧和困惑的。从这些方法的约定来看,thenApplyAsync中没有什么比thenApply更异步。

差异与在哪个线程上运行函数有关。提供给thenApply may run on any of the threads的功能

  1. 致电complete
  2. 在同一实例上致电thenApply

thenApplyAsync使用默认的Executor(也称为线程池)或提供的Executor

这些函数的异步部分与以下事实有关:异步操作最终会调用completecompleteExceptionally。这个想法来自Javascript,与多线程无关。

答案 2 :(得分:0)

第二步(即计算)将始终在第一步之后执行。

  

如果第二步必须等待第一步的结果,那么异步的意义何在?

在这种情况下,异步意味着您可以保证该方法将快速返回并且计算将在另一个线程中执行。

在调用thenApply时(没有异步),则没有这样的保证。在这种情况下,如果CompletableFuture在调用方法之前已经完成,则可以同步执行计算,即在调用thenApply的同一线程中执行。但是,计算也可以由完成未来的线程或在同一CompletableFuture上调用某个方法的某个其他线程异步执行。答案:https://stackoverflow.com/a/46062939/1235217详细解释了thenApply能做什么和不能保证什么。

那么,您什么时候应该使用thenApply?什么时候thenApplyAsync?我使用以下经验法则:

  • 非异步:仅当任务非常小且无阻塞时,因为在这种情况下,我们不在乎哪个线程可能执行该任务
  • 异步(通常使用显式执行程序作为参数):用于所有其他任务