instance of CompletableFuture cannot get expected result

隐身守侯 提交于 2019-12-08 08:04:17

问题


    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.


回答1:


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.




回答2:


you can suspend a CompletableFuture until completed by CompletableFuture#join, for example:

CompletableFuture<Void> stage = cf1.thenApply(v -> v + " b").thenAcceptAsync(v ->
        System.out.println(v)
);

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

//   v--- the main thread wait until the stage is completed
stage.join();


来源:https://stackoverflow.com/questions/44540862/instance-of-completablefuture-cannot-get-expected-result

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!