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

新手上路,请多包涵

假设我有以下代码:

 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

我在这个 博客 中读到,每个 thenApplyAsync 都在一个单独的线程中执行,并且“同时”执行(这意味着以下 thenApplyAsyncs 在前面 thenApplyAsyncs 之前开始) ,如果是这样,如果第一步没有完成,第二步的输入参数值是多少?

如果第二步不采取,第一步的结果会去哪里?第三步会取哪一步的结果?

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

这里 x -> x + 1 只是为了说明这一点,我想知道的是在计算时间很长的情况下。

原文由 Yulin 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.9k
2 个回答

不同之处在于 Executor 负责运行代码。 CompletableFuture 上的每个运营商一般有3个版本。

  1. thenApply(fn) - 在调用它的 CompleteableFuture 定义的线程上运行 fn ,所以你通常不知道它会在哪里执行。如果结果已经可用,它可能会立即执行。
  2. thenApplyAsync(fn) - 在环境定义的执行程序上运行 fn 无论情况如何。对于 CompletableFuture 这通常是 ForkJoinPool.commonPool()
  3. thenApplyAsync(fn,exec) - 在 fn 上运行 --- exec

最终结果是一样的,但调度行为取决于方法的选择。

原文由 Kiskae 发布,翻译遵循 CC BY-SA 3.0 许可协议

您错误地引用了文章的示例,因此您错误地应用了文章的结论。我在你的问题中看到两个问题:

.then___() 的正确用法是什么

在您引用的两个示例中,文章中没有,第二个函数必须等待第一个函数完成。 Whenever you call a.then___(b -> ...) , input b is the result of a and has to wait for a to complete, regardless of whether you use方法名为 Async 或不。该文章的结论不适用,因为您错误引用了它。

文章中的例子其实是

CompletableFuture<String> receiver = CompletableFuture.supplyAsync(this::findReceiver);

receiver.thenApplyAsync(this::sendMsg);
receiver.thenApplyAsync(this::sendMsg);

注意 thenApplyAsync 都应用于 receiver ,没有链接在同一个语句中。这意味着两个函数都可以启动一次 receiver 以未指定的顺序完成。 (任何顺序假设都取决于实现。)

更清楚地说:

a.thenApply(b).thenApply(c); means the order is a finishes then b starts, b finishes, then c starts.

a.thenApplyAsync(b).thenApplyAsync(c); will behave exactly the same as above as far as the ordering between a b c is concerned.

a.thenApply(b); a.thenApply(c); c b a bc 不必互相等待。

a.thenApplyAync(b); a.thenApplyAsync(c); 就顺序而言,工作方式相同。

在阅读以下内容之前,您应该了解以上内容。以上内容涉及异步编程,没有它您将无法正确使用 API。以下内容涉及线程管理,您可以使用它来优化您的程序并避免性能缺陷。但是如果不正确编写程序就无法优化程序。


如标题:Difference between thenApply and thenApplyAsync of Java CompletableFuture?

我必须指出,编写 JSR 的人一定混淆了“异步编程”这个技术术语,并选择了现在让新手和老手都感到困惑的名字。首先,从这些方法的约定来看, thenApplyAsync 没有比 thenApply 更异步的东西。

两者之间的区别与函数在哪个线程上运行有关。提供给 thenApply 的函数 可以在任何线程上运行

  1. 调用 complete
  2. 在同一实例上调用 thenApply

thenApplyAsync 的 2 个重载要么

  1. 使用默认值 Executor (又名线程池),或
  2. 使用提供的 Executor

带走的是 thenApply ,运行时承诺最终使用您无法控制的一些执行程序运行您的函数。如果您想要控制线程,请使用 Async 变体。

如果你的函数是轻量级的,那么哪个线程运行你的函数并不重要。

如果你的函数是 CPU 密集型的,你不想把它留给运行时。如果运行时选择网络线程来运行您的函数,则网络线程无法花时间处理网络请求,从而导致网络请求在队列中等待更长时间并且您的服务器变得无响应。在这种情况下,您想将 thenApplyAsync 与您自己的线程池一起使用。


有趣的事实:异步 != 线程

thenApply / thenApplyAsync , and their counterparts thenCompose / thenComposeAsync , handle / handleAsync , thenAccept / thenAcceptAsync , 都是异步的!这些函数的异步性质与异步操作 最终 调用 completecompleteExceptionally 的事实有关。这个想法来自 Javascript,它确实是异步的,但不是多线程的。

原文由 1283822 发布,翻译遵循 CC BY-SA 4.0 许可协议

推荐问题