Reputation: 41
I have been experimenting with Java 8 CompletableFutures. It was my understanding that calling CompletableFuture.supplyAsync(Supplier supplier, Executor executor) would always run a job in a thread provided by the passed in Executor, but I have noticed that it will sometimes run on the main thread when the supplier I pass to it is fairly "simple". My test code is:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class SyncTest {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(5);
CompletableFuture<?>[] cfs = new CompletableFuture<?>[10];
AtomicInteger mainCount = new AtomicInteger();
for (int i = 0; i < cfs.length; i++) {
int index = i;
CompletableFuture<Integer> cf =
CompletableFuture.supplyAsync(() -> {
return index;
}, pool).thenApply(j -> {
if (Thread.currentThread().getName().equals("main")) {
mainCount.incrementAndGet();
}
System.out.println(Thread.currentThread().getName() + ": " + index + ": doing a heavy computation");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return j;
});
cfs[i] = cf;
}
System.out.println(Thread.currentThread().getName() + " doing other stuff");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
CompletableFuture.allOf(cfs).join();
pool.shutdown();
System.out.println("Jobs on main: " + mainCount.get());
}
}
And the output I get is something along the lines of:
main: 0: doing a heavy computation
main: 1: doing a heavy computation
pool-1-thread-3: 2: doing a heavy computation
pool-1-thread-4: 3: doing a heavy computation
main: 4: doing a heavy computation
main doing other stuff
pool-1-thread-4: 9: doing a heavy computation
pool-1-thread-5: 8: doing a heavy computation
pool-1-thread-2: 7: doing a heavy computation
pool-1-thread-1: 6: doing a heavy computation
pool-1-thread-3: 5: doing a heavy computation
Jobs on main: 3
I understand that this is a fairly trivial example, and that there are other methods for CompletableFutures like thenSupplyAsync and completedFuture for working with them, I am more curious as to how its even possible that some of these tasks are executed on the main thread.
Upvotes: 3
Views: 3060
Reputation: 13525
the task which prints "doing a heavy computation" is called with thenApply
which means you did not specify the executor to run this task, and system is free to use any executor, including current thread.
If you want this job to execute on predefined executor, use thenApplyAsync
instead, with or without the second parameter - executor.
Upvotes: 3