Tharanga
Tharanga

Reputation: 387

Why CompletableFuture 's thenAccept() not running on the main thread

I process the long running operation inside the CompletableFuture's supplyAsync() and get the result into thenAccept(). In some times thenAccept() perform on the main thread but some time it running on the worker thread.But I want run thenAccept() operation only on the main thread. this is the sample code.

private void test() {

    ExecutorService executorService = Executors.newSingleThreadExecutor();

    CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println("supplyAsync | I am running on : " + Thread.currentThread().getName());
        return "Hello world";
    }, executorService);

    CompletableFuture<Void> cf3 = cf1.thenAccept(s -> {
        System.out.print("thenAccept | I am running on : " + Thread.currentThread().getName());
        System.out.println(" | answer : " + s);
    });

    cf3.thenRun(() -> {
        System.out.println("thenRun | I am running on : " + Thread.currentThread().getName());
        System.out.println();
    });

}

public static void main(String[] args) {

    App app = new App();
    for(int i = 0; i < 3; i++){
        app.test();
    }
}

result is :

supplyAsync | I am running on : pool-1-thread-1
thenAccept | I am running on : main | answer : Hello world
thenRun | I am running on : main

supplyAsync | I am running on : pool-2-thread-1
thenAccept | I am running on : main | answer : Hello world
thenRun | I am running on : main

supplyAsync | I am running on : pool-3-thread-1
thenAccept | I am running on : pool-3-thread-1 | answer : Hello world
thenRun | I am running on : pool-3-thread-1

How can i fix this ?

Upvotes: 5

Views: 5579

Answers (1)

Alexander
Alexander

Reputation: 1421

Take a look in the JavaDoc of CompletableFuture. The interesting part is the one about the CompletionStage policies.

There you find that using the non-async method results in a kind of either-or-scenario. If you then take a look in the implementation you will end up in the non-public part of the Java Runtime. There is some UNSAFE handling that implies that there may happen some kind of race condition.

I would suggest using thenAcceptAsync() and thenRunAsync() variants and pass your executorService variable to both calls.

Upvotes: 1

Related Questions