Java CompletableFuture 的 thenApply 和 thenApplyAsync 有什么区别?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/47489338/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the difference between thenApply and thenApplyAsync of Java CompletableFuture?
提问by Yulin
Suppose I have the following code:
假设我有以下代码:
CompletableFuture<Integer> future
= CompletableFuture.supplyAsync( () -> 0);
thenApply
case:
thenApply
案件:
future.thenApply( x -> x + 1 )
.thenApply( x -> x + 1 )
.thenAccept( x -> System.out.println(x));
Here the output will be 2. Now in case of thenApplyAsync
:
这里的输出将为 2。现在在以下情况下thenApplyAsync
:
future.thenApplyAsync( x -> x + 1 ) // first step
.thenApplyAsync( x -> x + 1 ) // second step
.thenAccept( x -> System.out.println(x)); // third step
I read in this blogthat each thenApplyAsync
are executed in a separate thread and 'at the same time'(that means following thenApplyAsyncs
started before preceding thenApplyAsyncs
finish), if so, what is the input argument value of the second step if the first step not finished?
我在这篇博客中读到,每个thenApplyAsync
都在一个单独的线程中执行,并且“同时”执行(这意味着thenApplyAsyncs
在thenApplyAsyncs
完成之前开始跟随),如果是这样,如果第一步没有完成,第二步的输入参数值是多少?
Where will the result of the first step go if not taken by the second step? the third step will take which step's result?
如果不采取第二步,第一步的结果将何去何从?第三步会走哪一步的结果?
If the second step has to wait for the result of the first step then what is the point of Async
?
如果第二步必须等待第一步的结果,那还有什么意义Async
呢?
Here x -> x + 1 is just to show the point, what I want know is in cases of very long computation.
这里 x -> x + 1 只是为了说明这一点,我想知道的是在计算时间很长的情况下。
采纳答案by Kiskae
The difference has to do with the Executor
that is responsible for running the code. Each operator on CompletableFuture
generally has 3 versions.
区别Executor
在于负责运行代码的那个。每个算子上CompletableFuture
一般有3个版本。
thenApply(fn)
- runsfn
on a thread defined by theCompleteableFuture
on which it is called, so you generally cannot know where this will be executed. It might immediately execute if the result is already available.thenApplyAsync(fn)
- runsfn
on a environment-defined executor regardless of circumstances. ForCompletableFuture
this will generally beForkJoinPool.commonPool()
.thenApplyAsync(fn,exec)
- runsfn
onexec
.
thenApply(fn)
-fn
在由CompleteableFuture
调用它的定义的线程上运行,因此您通常不知道它将在哪里执行。如果结果已经可用,它可能会立即执行。thenApplyAsync(fn)
-fn
无论情况如何,都在环境定义的执行程序上运行。对于CompletableFuture
这通常会ForkJoinPool.commonPool()
。thenApplyAsync(fn,exec)
-运行fn
上exec
。
In the end the result is the same, but the scheduling behavior depends on the choice of method.
最终结果是一样的,但调度行为取决于方法的选择。
回答by 1283822
You're misunderstanding the examples you quoted. In both examples, the second function has to wait for the first function to complete. 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 or not.
你误解了你引用的例子。在这两个示例中,第二个函数必须等待第一个函数完成。无论何时调用a.then___(b -> ...)
,输入b
都是 的结果a
并且必须等待a
完成,无论您是否使用 Async。
The actual example in the article is
文章中的实际例子是
CompletableFuture<String> receiver = CompletableFuture.supplyAsync(this::findReceiver);
receiver.thenApplyAsync(this::sendMsg);
receiver.thenApplyAsync(this::sendMsg);
Notice the thenApplyAsync
both applied on receiver
, not chained in the same statement. This means both function can start once receiver
completes, in an unspecified order. (Any assumption of order is implementation dependent.)
注意thenApplyAsync
两者都应用于receiver
,而不是链接在同一个语句中。这意味着两个函数都可以在receiver
完成后以未指定的顺序启动。(任何顺序假设都取决于实现。)
More technical explanation
更多技术说明
I must point out that thenApply
and thenApplyAsync
are terribly named and are confusing to the unfamiliar. There is nothing in thenApplyAsync
that is more asynchronous than thenApply
from the contract of these methods.
我必须指出,thenApply
和thenApplyAsync
被命名的可怕和感到困惑的陌生。没有什么thenApplyAsync
比thenApply
这些方法的契约更异步的了。
The difference between the two has to do with on which thread the function is run. The function supplied to thenApply
may run on any of the threadsthat
两者之间的区别与函数在哪个线程上运行有关。提供给函数thenApply
可以在任何线程运行该
- call
complete
- call
thenApply
on the same instance
- 称呼
complete
- 调用
thenApply
同一个实例
while thenApplyAsync
either uses a default Executor
(a.k.a. thread pool), or a supplied Executor
.
whilethenApplyAsync
要么使用默认值Executor
(又名线程池),要么使用提供的Executor
.
Asynchrony != threads
异步 != 线程
thenApply
/thenApplyAsync
, and their counterparts thenCompose
/thenComposeAsync
, handle
/handleAsync
, thenAccept
/thenAcceptAsync
, are all asynchronous! The asynchronous nature of these function has to do with the fact that an asynchronous operation eventuallycalls complete
or completeExceptionally
. The idea came from Javascript, which is indeed asynchronous but isn't multi-threaded.
thenApply
/ thenApplyAsync
,和它们对应的thenCompose
/ thenComposeAsync
,handle
/ handleAsync
,thenAccept
/ thenAcceptAsync
,都是异步的!这些函数的异步特性与异步操作最终调用complete
或的事实有关completeExceptionally
。这个想法来自 Javascript,它确实是异步的,但不是多线程的。
回答by Willi Mentzel
This is what the documentation says about CompletableFuture's
thenApplyAsync
:
这是文档所说的内容CompletableFuture's
thenApplyAsync
:
Returns a new CompletionStage that, when this stage completes normally, is executed using this stage's default asynchronous execution facility, with this stage's result as the argument to the supplied function.
返回一个新的 CompletionStage,当这个阶段正常完成时,使用这个阶段的默认异步执行工具执行,这个阶段的结果作为提供的函数的参数。
So, thenApplyAsync
has to wait for the previous thenApplyAsync's
result:
所以,thenApplyAsync
必须等待之前的thenApplyAsync's
结果:
In your case you first do the synchronous work and then the asynchronous one. So, it does not matter that the second one is asynchronous because it is started only after the synchrounous work has finished.
在您的情况下,您首先进行同步工作,然后进行异步工作。因此,第二个是异步的并不重要,因为它只有在同步工作完成后才启动。
Let's switch it up. In some cases "async result: 2" will be printed first and in some cases "sync result: 2" will be printed first. Here it makes a difference because both call 1 and 2 can run asynchronously, call 1 on a separate thread and call 2 on some other thread, which might be the main thread.
让我们切换它。在某些情况下,“async result: 2”将首先打印,而在某些情况下,“sync result: 2”将首先打印。这里有区别,因为调用 1 和调用 2 都可以异步运行,在单独的线程上调用 1 并在其他线程上调用 2,该线程可能是主线程。
CompletableFuture<Integer> future
= CompletableFuture.supplyAsync(() -> 0);
future.thenApplyAsync(x -> x + 1) // call 1
.thenApplyAsync(x -> x + 1)
.thenAccept(x -> System.out.println("async result: " + x));
future.thenApply(x -> x + 1) // call 2
.thenApply(x -> x + 1)
.thenAccept(x -> System.out.println("sync result:" + x));
回答by Stefan Feuerhahn
The second step (i.e. computation) will always be executed after the first step.
第二步(即计算)将始终在第一步之后执行。
If the second step has to wait for the result of the first step then what is the point of Async?
如果第二步必须等待第一步的结果,那么 Async 的意义何在?
Async means in this case that you are guaranteed that the method will return quickly and the computation will be executed in a different thread.
在这种情况下,异步意味着您可以保证该方法将快速返回并且计算将在不同的线程中执行。
When calling thenApply
(without async), then you have no such guarantee. In this case the computation may be executed synchronously i.e. in the same thread that calls thenApply
if the CompletableFuture is already completed by the time the method is called. But the computation may also be executed asynchronously by the thread that completes the future or some other thread that calls a method on the same CompletableFuture
. This answer: https://stackoverflow.com/a/46062939/1235217explained in detail what thenApply does and does not guarantee.
当调用thenApply
(没有异步)时,你就没有这样的保证。在这种情况下,计算可以同步执行,即thenApply
如果 CompletableFuture 在调用方法时已经完成,则在调用的同一线程中执行。但是计算也可以由完成 future 的线程或调用相同CompletableFuture
.方法的其他线程异步执行。这个答案:https: //stackoverflow.com/a/46062939/1235217 详细解释了 thenApply 做什么和不保证什么。
So when should you use thenApply
and when thenApplyAsync
? I use the following rule of thumb:
那么什么时候应该使用thenApply
,什么时候使用thenApplyAsync
?我使用以下经验法则:
- non-async: only if the task is very small and non-blocking, because in this case we don't care which of the possible threads executes it
- async (often with an explicit executor as parameter): for all other tasks
- 非异步:仅当任务非常小且非阻塞时,因为在这种情况下我们不关心哪个可能的线程执行它
- 异步(通常以显式执行器作为参数):用于所有其他任务