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:
runAsync
-> thenRunAsync+e
-> ...runAsync
-> thenRun
-> ...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();
}
}
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.