Search code examples
javacompletable-future

Why CompletableFuture did not execute in sequence?


My goal is to understand how CompletableFuture works.

My expected result: If I do CompletableFuture.runAsync().thenRun().thenRunAsync(). The thread will be executed in sequence runAsync() -> thenRun() -> thenRunAsync().

My actual result: The sequence is race condition. Sometimes:

  1. runAsync -> thenRunAsync+e -> ...
  2. runAsync -> thenRun -> ...

Reference from SO

public class RunExample5 {
  public static void main(String[] args) {
    ExecutorService e = Executors.newSingleThreadExecutor(r -> new Thread(r, "sole thread"));
    CompletableFuture<?> f = CompletableFuture.runAsync(() -> {
              System.out.println("runAsync:\t" + Thread.currentThread());
              LockSupport.parkNanos((int) 1e9);
            }, e);
    f.thenRun(() -> System.out.println("thenRun:\t" + Thread.currentThread()));
    f.thenRunAsync(() -> System.out.println("thenRunAsync:\t" + Thread.currentThread()));
    f.thenRunAsync(() -> System.out.println("thenRunAsync+e:\t" + Thread.currentThread()),
            e);
    LockSupport.parkNanos((int) 2e9);
    e.shutdown();
  }
}

Solution

  • You need

    f.thenRun(() -> System.out.println("thenRun:\t" + Thread.currentThread()))
        .thenRunAsync(() -> System.out.println("thenRunAsync:\t" + Thread.currentThread()))
        .thenRunAsync(() -> System.out.println("thenRunAsync+e:\t" + Thread.currentThread()), e);
    

    The interface to CompletableFuture doesn't work quite the way you're imagining. f itself doesn't keep track of every call to thenRun or thenRunAsync and run them in order; instead, it treats everything from thenRun or thenRunAsync as simultaneously runnable as soon as the main work completes. If you want to chain more complicated sequences of work, you need to use the return value of thenRun or thenRunAsync -- a CompletionStage object -- and call thenRunAsync on that.