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

Yul*_*lin 34 java completable-future

假设我有以下代码:

CompletableFuture<Integer> future  
        = CompletableFuture.supplyAsync( () -> 0);
Run Code Online (Sandbox Code Playgroud)

thenApply 案件:

future.thenApply( x -> x + 1 )
      .thenApply( x -> x + 1 )
      .thenAccept( x -> System.out.println(x));
Run Code Online (Sandbox Code Playgroud)

这里输出为2.现在如下thenApplyAsync:

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

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

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

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

这里x - > x + 1只是为了表明这一点,我想知道的是在非常长的计算情况下.

Kis*_*kae 27

不同之处Executor在于负责运行代码.每个操作员CompletableFuture通常有3个版本.

  1. thenApply(fn)- fnCompleteableFuture调用它的on上定义的线程上运行,因此通常无法知道它将在何处执行.如果结果已经可用,它可能会立即执行.
  2. thenApplyAsync(fn)- fn无论在何种情况下,都在环境定义的执行程序上运行.对于CompletableFuture这通常会ForkJoinPool.commonPool().
  3. thenApplyAsync(fn,exec)-运行fnexec.

最后结果是相同的,但调度行为取决于方法的选择.

  • 很好的答案,很好地得到关于`thenApply`的所有差异版本的解释.但是我想你错过了这个问题的一部分:"有什么意义?" 何时以及为什么会使用`thenApplyAsync`而不是`thenApply`?他们会在同一时间运行,不是吗?差别似乎只是它们运行的​​线程,但这应该不重要,因为两者都可能在某个线程池线程上运行. (5认同)
  • 它是一个链,链中的每个调用都取决于前一部分是否完成。如果您需要在预定义的执行器上执行该函数,您可以使用 `thenApplyAsync`。一个例子是在处理 UI 更新作为对未来结果的响应时在 UI 线程上执行。 (3认同)
  • 确保“ thenApplyAsync”不阻塞当前线程,而“ thenApply”取决于其行为被调用的对象。 (2认同)

128*_*822 24

你错误地引用了文章的例子,所以你错误地应用了文章的结论。我在你的问题中看到两个问题:

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

在您引用的两个示例中(文章中没有),第二个函数必须等待第一个函数完成。无论何时调用a.then___(b -> ...), inputb都是 的结果,a并且必须等待a完成,无论您是否使用命名的方法Async。这篇文章的结论不适用,因为你错误地引用了它。

文章中的例子其实是

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

receiver.thenApplyAsync(this::sendMsg);  
receiver.thenApplyAsync(this::sendMsg);  
Run Code Online (Sandbox Code Playgroud)

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

更清楚地说:

a.thenApply(b).thenApply(c);意味着顺序是a完成然后b开始,b完成,然后c开始。
a.thenApplyAsync(b).thenApplyAsync(c);就排序a b c而言,行为与上述完全相同。

a.thenApply(b); a.thenApply(c);意味着a完成,然后bc可以开始,以任何顺序。b并且c不必互相等待。
a.thenApplyAync(b); a.thenApplyAsync(c);就顺序而言,工作方式相同。

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


正如标题:Java CompletableFuturethenApplythenApplyAsync的区别?

我必须指出,编写 JSR 的人一定混淆了技术术语“异步编程”,并选择了现在让新手和老手都感到困惑的名称。首先,没有什么thenApplyAsyncthenApply这些方法的契约更异步的了。

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

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

而其中thenApplyAsync任何一个的 2 个重载

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

需要注意的是,对于thenApply,运行时承诺最终会使用一些您无法控制的执行程序来运行您的函数。如果您想控制线程,请使用 Async 变体。

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

如果您的函数受到大量 CPU 限制,您不希望将它留给运行时。如果运行时选择网络线程来运行您的函数,则网络线程无法花时间处理网络请求,从而导致网络请求在队列中等待更长时间,并且您的服务器变得无响应。在这种情况下,您想使用thenApplyAsync自己的线程池。


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

thenApply/ thenApplyAsync,和它们的对应物thenCompose/ thenComposeAsynchandle/ handleAsyncthenAccept/ thenAcceptAsync,都是异步的!这些函数的异步特性与异步操作最终调用complete或的事实有关completeExceptionally。这个想法来自 Javascript,它确实是异步的,但不是多线程的。

  • 而 thenApplyAsync 要么使用默认的执行器(又名线程池),&lt;----你知道那是哪个默认线程池吗?@1283822 (2认同)