简体   繁体   中英

Difference between these 2 methods

I am not able to spot why these 2 method behave differently. Only difference between the two is the sleep method call.

In the first method, I was expecting that second future execution will wait for first future to complete as it is dependent on the result of the first.

Please help me to understand why the behaviour is different for both the method or is there anything logically wrong in my code?

I have tried running in debug mode but still it gave the same result.

public static void thenComposeWithSleep() {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return "Hello";
        }).thenCompose(value -> CompletableFuture.supplyAsync(() -> value + " Then compose is been called"));
        completableFuture.thenAccept(System.out::println);
    }

public static void thenCompose() {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello")
                .thenCompose(value -> CompletableFuture.supplyAsync(() -> value + " Then compose is been called"));
        completableFuture.thenAccept(System.out::println);
    }

Expected Output from first method : "Hello Then compose is been called".

Actual Output from first method : Empty String

Expected & Actual Output from second method are same.

Output : "Hello Then compose is been called"

The problem here is that

CompletableFuture.supplyAsync()

hands execution to

ForkJoinPool.commonPool()

When this runs in daemon mode (which is default according to the Java7 documentation), if your main thread terminates all the uncompleted async tasks will be discarded and never complete.

So my guess is what you are assuming as an "Empty String" is actually

System.out::println

is not executing at all.

To illustrate this for the class

public class DaemonsAtPlay {

    public static void thenComposeWithSleep() {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return "Slept"; //HERE
        }).thenCompose(value -> CompletableFuture.supplyAsync(() -> value + " Then compose is been called"));
        completableFuture.thenAccept(DaemonsAtPlay::report); //HERE
    }

    public static void thenCompose() {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello")
                .thenCompose(value -> CompletableFuture.supplyAsync(() -> value + " Then compose is been called"));
        completableFuture.thenAccept(DaemonsAtPlay::report); //HERE
    }

    private static final AtomicInteger counter = new AtomicInteger();

    static void report(String msg) {
        System.out.println("report: " + counter.incrementAndGet() + "; message: >" + msg + "<");
    }

    public static void executeMultiple(int iterations, boolean withsleep) {
        for(int i=0; i<iterations; ++i) {
            if(withsleep) {
                thenComposeWithSleep();
            } else {
                thenCompose();
            }
        }
    }

    public static void main(String... none) throws Exception {
        executeMultiple(100, false);
        executeMultiple(100, true);
        report("exiting main");
    }
}

I get the output

report: 1; message: >Hello Then compose is been called<
report: 2; message: >Hello Then compose is been called<
report: 3; message: >Hello Then compose is been called<
report: 4; message: >Hello Then compose is been called<
report: 5; message: >Hello Then compose is been called<
report: 6; message: >Hello Then compose is been called<
report: 7; message: >Hello Then compose is been called<
report: 8; message: >Hello Then compose is been called<
report: 9; message: >Hello Then compose is been called<
report: 10; message: >Hello Then compose is been called<
report: 11; message: >Hello Then compose is been called<
report: 12; message: >Hello Then compose is been called<
report: 13; message: >Hello Then compose is been called<
report: 14; message: >Hello Then compose is been called<
report: 15; message: >Hello Then compose is been called<
report: 16; message: >Hello Then compose is been called<
report: 17; message: >Hello Then compose is been called<
report: 18; message: >Hello Then compose is been called<
report: 19; message: >Hello Then compose is been called<
report: 20; message: >Hello Then compose is been called<
report: 21; message: >Hello Then compose is been called<
report: 22; message: >exiting main<

Which illustrates that not only the 'slept' tasks but many of the 'unslept' tasks didn't terminate.

If you want to wait for all the CompletableFuture tasks to terminate you can use

CompletableFuture.join()

on the main thread.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM