Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
371 views
in Technique[技术] by (71.8m points)

java - instance of CompletableFuture cannot get expected result

    CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println("enter into completableFuture()");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("start to out of completableFuture()");
        return "a";
    });

    System.out.println("do something else");

    cf1.thenApply(v -> v + " b").thenAcceptAsync(v ->
            System.out.println(v)
    );

    System.out.println("finalize...");

    //cannot get expected result, if this line was comment out.
    //TimeUnit.SECONDS.sleep(10);

code as above.

when writing a example of using CompletableFuture in jdk8, I got confused.

I must add the last line

TimeUnit.SECONDS.sleep(10);

to get the expected result.

I would like to know whether the program has ended if I do not let its main thread to sleep. if not, why I cannot get the output?

really thanks for you time.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

A JVM terminates when no non-daemon threads are running, so if asynchronous operations are executed by daemon threads only, it will terminate when the main thread terminates, not continuing the background operations.

There are several ways to solve this.

  1. If the background computations form a single dependency chain, you can use the last operation to wait for its completion, as its completion implies the completion of all previous stages. Letting the main thread wait until the completion defers the JVM’s termination to that point:

    CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println("enter into completableFuture()");
        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
        System.out.println("start to out of completableFuture()");
        return "a";
    });
    System.out.println("do something else");
    CompletableFuture last
        = cf1.thenApply(v -> v + " b").thenAcceptAsync(System.out::println);
    System.out.println("finalize...");
    last.join();
    
  2. Consider the documentation of CompletableFuture:

    All async methods without an explicit Executor argument are performed using the ForkJoinPool.commonPool() (unless it does not support a parallelism level of at least two, in which case, a new Thread is created to run each task).

    Since it’s the property of that F/J common pool to use daemon threads, we can use that knowledge to wait for the completion of all pending tasks in that case, which works independently of the dependencies between these pending tasks:

    CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println("enter into completableFuture()");
        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
        System.out.println("start to out of completableFuture()");
        return "a";
    });
    System.out.println("do something else");
    cf1.thenApply(v -> v + " b").thenAcceptAsync(System.out::println);
    System.out.println("finalize...");
    if(ForkJoinPool.getCommonPoolParallelism()>1)
        ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);
    
  3. Use an explicit executor, which will not use daemon threads. The thread pool executors provided by the JRE, letting ForkJoinPool aside, use non-daemon threads by default:

    final ExecutorService threadPool = Executors.newCachedThreadPool();
    CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println("enter into completableFuture()");
        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
        System.out.println("start to out of completableFuture()");
        return "a";
    }, threadPool);
    System.out.println("do something else");
    cf1.thenApply(v -> v + " b").thenAcceptAsync(System.out::println);
    System.out.println("finalize...");
    threadPool.shutdown();
    

    Note that threadPool.shutdown(); does not imply waiting nor does it stop pending tasks; it only stops accepting new tasks and ensures that the pooled threads will terminate once all pending tasks have been processed. You could place it directly after using it with supplyAsync, without changing the behavior.

    So the third solution is the only one letting the main thread exit, with the JVM continuing until all pending background tasks are processed, as they are running in non-daemon threads.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...