Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CompletableFuture not working as expected

I am trying to understand the working of CompletableFuture from Java 8. Below code works as expected

CompletableFuture.supplyAsync(() -> {
    System.out.println("supplyAsync Thread name " + Thread.currentThread().getName());
    return "str";
}).thenApply(str -> {
    System.out.println("thenApply Thread name " + Thread.currentThread().getName());
    return str;
}).thenApply(str1 -> {
    System.out.println("thenApply Thread name " + Thread.currentThread().getName());
    return str1;
}).thenAccept(str3 -> {
    System.out.println("thenAccept Thread name " + Thread.currentThread().getName());
});
System.out.println("Thread name " + Thread.currentThread().getName());

Output:

supplyAsync Thread name ForkJoinPool.commonPool-worker-1
thenApply Thread name main
thenApply Thread name main
thenAccept Thread name main
Thread name main

But when I put in some computation, it doesn't work as expected please correct me if I am missing something.

CompletableFuture.supplyAsync(() -> {
    System.out.println("supplyAsync Thread name " + Thread.currentThread().getName());
    long val = 0;
    for (long i = 0; i < 1000000; i++) {
        val++;
    }
    return "str";
}).thenApply(str -> {
    System.out.println("thenApply Thread name " + Thread.currentThread().getName());
    long val = 0;
    for (long i = 0; i < 1000000; i++) {
        val++;
    }
    return str;
}).thenApply(str1 -> {
    System.out.println("thenApply Thread name " + Thread.currentThread().getName());
    long val = 0;
    for (long i = 0; i < 1000000; i++) {
        val++;
    }
    return str1;
}).thenAccept(str3 -> {
    System.out.println("thenAccept Thread name " + Thread.currentThread().getName());
    long val = 0;
    for (long i = 0; i < 1000000; i++) {
        val++;
    }
});

System.out.println("Thread name " + Thread.currentThread().getName());

Output is:

supplyAsync Thread name ForkJoinPool.commonPool-worker-1
Thread name main

I agree that I am not joining the child thread to main thread. My understanding is child thread should print the statements independently of main thread. The question is why is it not printing at all.

like image 573
Vishwanath Joshi Avatar asked Dec 14 '18 06:12

Vishwanath Joshi


People also ask

Is CompletableFuture get blocking?

The CompletableFuture. get() method is blocking. It waits until the Future is completed and returns the result after its completion.

Can a Completable Future be null?

Since Void can not be instantiated, you can only complete a CompletableFuture<Void> with a null result, which is exactly what you also will get when calling join() on the future returned by allOf() once it has been successfully completed. CompletableFuture<Void> cf = CompletableFuture.

What is the difference between Future and CompletableFuture?

While using Future, we do not get notified when it is complete neither does it provides us a callable method which will automatically be called when the result is available but CompletableFuture provides us with a lot of callable methods which can be used as per our use case.

Can you describe how Java CompletableFuture works?

A CompltableFuture is used for asynchronous programming. Asynchronous programming means writing non-blocking code. It runs a task on a separate thread than the main application thread and notifies the main thread about its progress, completion or failure.


1 Answers

Explanation

You're not joining the child thread ForkJoinPool.commonPool-worker-1 to the main thread. So it gets killed once thread main finishes.

Solution

Try calling .join() on your completable future at some point in your code. Note that this method is blocking the main thread. So the execution after the join point will be suspended until the child thread finishes its execution.

CompletableFuture.supplyAsync(() -> {
    System.out.println("=> supplyAsync Thread name " + Thread.currentThread().getName());
    // ...
}).thenApply(str -> {
    System.out.println("thenApply Thread name " + Thread.currentThread().getName());
    // ...
}).thenApply(str1 -> {
    System.out.println("thenApply Thread name " + Thread.currentThread().getName());
    // ...
}).thenAccept(str3 -> {
    System.out.println("thenAccept Thread name " + Thread.currentThread().getName());
    // ...
}).join()

Will print:

supplyAsync Thread name ForkJoinPool.commonPool-worker-1
thenApply Thread name ForkJoinPool.commonPool-worker-1
thenApply Thread name ForkJoinPool.commonPool-worker-1
thenAccept Thread name ForkJoinPool.commonPool-worker-1
Thread name main

If you want the last System.out.println(...) not depend from execution of the child thread then assign your CompletableFuture to a variable and join it in the very end of main:

CompletableFuture<Void> future = CompletableFuture.supplyAsync(...) ... // 

System.out.println("Thread name " + Thread.currentThread().getName());

future.join();    
like image 134
ETO Avatar answered Oct 18 '22 05:10

ETO