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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…