What is the difference between thenApply and thenApplyAsync of Java CompletableFuture?

ぐ巨炮叔叔 提交于 2019-12-03 06:32:30

问题


Suppose I have the following code:

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

thenApply case:

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:

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 blog that 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?

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?

Here x -> x + 1 is just to show the point, what I want know is in cases of very long computation.


回答1:


The difference has to do with the Executor that is responsible for running the code. Each operator on CompletableFuture generally has 3 versions.

  1. thenApply(fn) - runs fn on a thread defined by the CompleteableFuture 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.
  2. thenApplyAsync(fn) - runs fn on a environment-defined executor regardless of circumstances. For CompletableFuture this will generally be ForkJoinPool.commonPool().
  3. thenApplyAsync(fn,exec) - runs fn on exec.

In the end the result is the same, but the scheduling behavior depends on the choice of method.




回答2:


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.

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 reciver, 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.)


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.

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 threads that

  1. call complete
  2. call thenApply on the same instance

while thenApplyAsync either uses a default Executor (a.k.a. thread pool), or a supplied Executor.

Asynchrony != threads

thenApply/thenApplyAsync, and their counterparts thenCompose/thenComposeAsync, handle/handleAsync, thenAccept/thenAcceptAsync, are all asynchronous! The asynchronous part of these function has to do with the fact that an asynchronous operation eventually calls complete or completeExceptionally. The idea came from Javascript, which is indeed asynchronous but isn't multi-threaded.




回答3:


This is what the documentation says about 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.

So, thenApplyAsync has to wait for the previous thenApplyAsync's result:

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.

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));



回答4:


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 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/1235217 explained in detail what thenApply does and does not guarantee.

So when should you use thenApply and when thenApplyAsync? I use the following rule of thumb:

  • 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


来源:https://stackoverflow.com/questions/47489338/what-is-the-difference-between-thenapply-and-thenapplyasync-of-java-completablef

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!