As @nullpointer points out , the documentation tells you what you need to know. However, the relevant text is surprisingly vague, and some of the comments (and answers) posted here seem to rely on assumptions that are not supported by the documentation. Thus, I consider it appropriate to highlight it. In particular, we should carefully read this paragraph:
The actions provided for dependent terminations of non-asynchronous methods can be performed by a thread that completes the current CompletableFuture or by any other caller of the termination method.
It sounds simple enough, but it highlights the details. Apparently, he deliberately avoids describing when a dependent termination can be called in a terminating thread or while invoking a termination method such as thenApply . As written, the above paragraph will actually ask us to fill in the blanks with assumptions. This is dangerous, especially when it comes to parallel and asynchronous programming, where many of the expectations that we have developed when programmers find themselves on their head. Let's take a closer look at what says .
The documentation does not state that dependent refinements registered before calling complete() will be executed in the final chain. Moreover, while it states that dependent termination can be caused by calling a termination method such as thenApply , it does not indicate that termination will be called in the thread that registers it (note the words βany otherβ) .
These are potentially important points for those who use CompletableFuture to plan and write tasks. Consider this sequence of events:
- Thread A registers dependent termination via
f.thenApply(c1) . - After a while, Thread B calls
f.complete() . - Around the same time, Thread C registers another dependent termination through
f.thenApply(c2) .
Conceptually, complete() does two things: it publishes the result of the future, and then it tries to invoke dependent refinements. Now, what happens if Thread C starts after publishing the result value, but before Thread B comes close to calling c1 ? Depending on the implementation, Thread C can see that f complete, and it can then call c1 and c2 . Alternatively, Thread C can call c2 , leaving Thread B to call c1 . The documentation does not exclude the possibility. Keeping this in mind, here are the assumptions that are not supported by the documentation:
- The completion of the dependent completion of
c , registered on f before completion, will be called during the call to f.complete() ; - That
c will be completed by the time f.complete() returns; - These dependent additions will be called in any particular order (for example, registration procedure);
- All dependent completions registered before completion of
f completed, before completions are completed after completion of f .
Consider another example:
- Thread A calls
f.complete() ; - After a while, Thread B registers completion through
f.thenApply(c1) ; - Around the same time, Thread C registers a single termination through
f.thenApply(c2) .
If it is known that f has already been completed, it may be tempting to assume that c1 will be called during f.thenApply(c1) and that c2 will be called during f.thenApply(c2) . It can also be assumed that c1 will be completed by the time f.thenApply(c1) returns. However, the documentation does not support these assumptions. It is possible that one of the threads calling thenApply terminates the call to both c1 and c2 , while the other thread does not call anyone.
A thorough analysis of the JDK code could determine how hypothetical scenarios can be illustrated above. But even this is risky because you can rely on implementation details that are (1) not portable, or (2) subject to change. It is best not to assume anything that is not specified in javadocs or the original JSR specification.
TL; DR: Be careful what you expect, and when you write documentation, be as clear and thoughtful as possible. Although brevity is a wonderful thing, be careful with the human tendency to fill in the blanks.