Search code examples
javamultithreadingasynchronouscompletable-future

CompletableFuture join() doesn't seem to wait for completion


I am facing an issue where I am executing two CompletableFuture objects sequentially. My intent is for the first one to complete, and only after that to begin executing the second one. So in other words, the second is dependent on completion of the first.

I cannot share the exact code, but here is a simplified version of what I'm doing:

public static void main() {
    Set<Object> firstSet = /* ... */
    Set<Object> secondSet = /* ... */

    CompletableFuture<Void> firstFuture = getFuture(firstSet);
    CompletableFuture<Void> secondFuture = getFuture(secondSet);

    // Want to ensure that secondFuture does not begin executing until 
    // all of the futures in firstFuture have completed 
    firstFuture.join();
    secondFuture.join();
}

CompletableFuture<Void> getFuture(Set<Object> listOfObjects) {

    List<CompletableFuture<Void>> futures = listOfObjects
    .stream()
    .map(object -> CompletableFuture.runAsync(getRunnableForObject(object), executorService))
    .collect(Collectors.toList());

    CompletableFuture<Void> future = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
    return future;
}

Runnable getRunnableForObject(Object obj) {
   // assume this returns a custom runnable whose execution behavior depend on the object parameter
}

When running the code, I see that sometimes logs printed during execution of firstFuture.join() have a timestamp that is later than some logs printed during secondFuture.join(). My expectation is that we should never see any logs printed during secondFuture come before any logs printed during firstFuture.

I thought firstFuture.join() ensures the future completes in its entirety before synchronously moving onto secondFuture.join(), but maybe I am understanding wrong. Can anyone advise?


Solution

  • This code kicks off background tasks for both sets so they are in progress at same time:

    CompletableFuture<Void> firstFuture = getFuture(firstSet);
    CompletableFuture<Void> secondFuture = getFuture(secondSet);
    

    An easy way to ensure first / second order just switch these lines to:

    CompletableFuture<Void> firstFuture = getFuture(firstSet);
    firstFuture.join();
    
    CompletableFuture<Void> secondFuture = getFuture(secondSet);
    secondFuture.join();