CompletableFuture的实例无法获得预期的结果

时间:2017-06-14 09:34:45

标签: java java-8 completable-future

    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);

代码如上。

在jdk8中编写使用CompletableFuture的示例时,我感到很困惑。

我必须添加最后一行

TimeUnit.SECONDS.sleep(10);

获得预期结果。

如果我不让它的主线程进入休眠状态,我想知道程序是否已经结束。如果没有,为什么我不能得到输出?

非常感谢你的时间。

2 个答案:

答案 0 :(得分:3)

当没有非守护程序线程正在运行时,JVM终止,因此如果异步操作仅由守护程序线程执行,它将在主线程终止时终止,而不是继续后台操作。

有几种方法可以解决这个问题。

  1. 如果后台计算形成单个依赖关系链,则可以使用最后一个操作来等待其完成,因为它的完成意味着所有先前阶段的完成。让主线程等到完成将JVM的终止推迟到该点:

    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. 考虑documentation of CompletableFuture

      

    使用ForkJoinPool.commonPool()执行没有显式Executor参数的所有 async 方法(除非它不支持至少两个的并行级别,在这种情况下,创建一个新的Thread运行每个任务。)

    由于该F / J公共池的属性是使用守护程序线程,因此在这种情况下,我们可以使用该知识等待所有待处理任务的完成,这与这些待处理任务之间的依赖关系无关:

    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. 使用显式执行程序,它不会使用守护程序线程。 JRE提供的线程池执行程序,让ForkJoinPool放在一边,默认情况下使用非守护程序线程:

    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();
    

    请注意threadPool.shutdown();并不意味着等待,也不会停止待处理的任务;它只会停止接受新任务,并确保在处理完所有待处理任务后池池线程将终止。您可以在使用supplyAsync后直接放置它,而不会改变行为。

    因此,第三个解决方案是让main线程退出的唯一解决方案,JVM将继续处理,直到处理完所有挂起的后台任务,因为它们在非守护程序线程中运行。

答案 1 :(得分:0)

您可以暂停CompletableFuture,直到CompletableFuture#join完成,例如:

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();