Welcome to Qi-U Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
230 views
in Technique[技术] by (71.8m points)

java - Will a chain of method calls (CompletableFuture API) execute asynchronously if the first method in a chain is asynchronous?

I'm studying CompletableFuture API and there is an example:

CompletableFuture.completedFuture(url)
                 .thenComposeAsync(this::readPage, executor)
                 .thenApply(this::getImageURLs)
                 .thenApply(this::saveFoundImages)
                 .....

I have a question: if I call the thenComposeAsync(...) method as the first one, will the other methods in the chain execute in the executor which I passed through the params, or I should call the other methods using async to get asynchronous execution in a particular executor?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

Please log in or register to answer this question.

1 Answer

0 votes
by (71.8m points)

OK, so there are 3 types of methods in CompletableFuture. For example:

  • thenApply()
  • thenApplyAsync(Function) (without an Executor)
  • thenApplyAsync(Function, Executor) (with an Executor)

The last one means that this action will execute in the Executor that you pass to it and it is the most obvious one.

The second one means that the action is executed in the ForkJoinPool.

The first one is far more interesting. The documentation makes it sound like it's easy, via:

Actions supplied for dependent completions of non-async methods may be performed by the thread that completes the current CompletableFuture, or by any other caller of a completion method

And you need to start bisecting this into smaller pieces. What you need to understand that there are threads that complete a certain CompletableFuture, there are threads that execute some actions on it and there are threads that chain certain dependent actions. Potentially, these are all different threads. And this is where it all begins:

  • If the dependent action is already chained, the thread that will call complete is going to be the thread that executes this action.

  • If the future is already completed, then the thread that chains the action will execute it.

Since there is no linear actions on the steps above, it is literally impossible to say for sure in which thread your thenApply will execute, at least with 100% certainty. That action can be executed in any of :

  • the thread that calls complete/completeExceptionally
  • the thread that does the chaining of thenApply
  • the thread that calls join/get

Any of the above is a possibility. If you really want I made a rather interesting test here, proving some of the things above.


I am not trying to pick on the other answer, but he made a rather interesting point that I was very confused about in the begging too:

In your example: After .thenComposeAsync also all the following chained futures will get completed by executor.

We can easily prove that this is not correct:

 CompletableFuture<String> future1 = CompletableFuture.completedFuture("a");
 CompletableFuture<String> future2 = future1.thenApplyAsync(x -> "b" + x, Executors.newCachedThreadPool());

 LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));

 CompletableFuture<String> future3 = future2.thenApply(x -> {
      System.out.println(Thread.currentThread().getName());
      return x + "c";
 });
 future3.join();

What you are going to see if you run this, is that main actually executes thenApply, not a thread from the pool.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Qi-U Community for programmer and developer-Open, Learning and Share
...