Alexander Guldemond
Alexander Guldemond

Reputation: 41

CompletableFuture supply Async sometimes runs on main thread

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

Answers (1)

Alexei Kaigorodov
Alexei Kaigorodov

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

Related Questions