【问题标题】:Nested Futures not executing嵌套期货未执行
【发布时间】:2017-07-09 14:22:28
【问题描述】:

我遇到了一个奇怪的情况。我在摆弄CompletableFuture,当运行以下代码时,我得到了意想不到的结果:

public static void main(String[] args) {     
    CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<Object>>>>>> completableFutureCompletableFuture = CompletableFuture.supplyAsync(() -> {
        System.out.println("first");
        return CompletableFuture.supplyAsync(() -> {
            System.out.println("second");
            return CompletableFuture.supplyAsync(() -> {
                System.out.println("third");
                return CompletableFuture.supplyAsync(() -> {
                    System.out.println("fourth");
                    return CompletableFuture.supplyAsync(() -> {
                        System.out.println("fifth");
                        return CompletableFuture.completedFuture(null);
                    });
                });
            });
        });
    });

   completableFutureCompletableFuture.get();
}

没有抛出异常(即使使用exceptionally),我看到的是控制台输出是

first
second
third // appears sometimes

现在,显然这段代码没有真正的生产价值,但这代表了一种情况,即您的代码具有未知数量的嵌套,其中每个或其中一些嵌套创建 CompleteableFutures 不会被执行。

任何解释(以及如何修复的示例)将不胜感激

【问题讨论】:

标签: java multithreading completable-future


【解决方案1】:

这不起作用的原因是因为在您的简单测试中,VM 在所有任务完成之前退出。

当您调用completableFutureCompletableFuture.get() 时,只有第一次嵌套的future 才能保证完成。虚拟机退出,所有线程都被杀死。

换句话说,第一个嵌套的未来可能仍然是“未完成的”,因为它的线程可能仍然很忙。但是,当您尝试使用get 获取结果时,它当然会等到它完成并且它会按预期工作。试试看:

completableFutureCompletableFuture.get().get().get().get().get()

...然后你强制所有期货都完成并且一切都按预期工作。

【讨论】:

    【解决方案2】:

    发生这种情况是因为您的 CompletableFuture 是异步执行的,但您的程序在第五次调用发生之前终止(我假设您在单个主程序中运行它并在创建期货后立即返回)。

    因为您可能不知道 Future 中堆叠了多少 Future(由于类型擦除)。您可能想要执行递归 .get()。

    见:

    public static void main(String[] args) throws InterruptedException, ExecutionException {
    
        CompletableFuture<?> futures = getFutures();
        recursiveGet(futures);
        System.out.println("finished");
    
    }
    
    public static CompletableFuture<?> getFutures() {
        CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<CompletableFuture<Object>>>>>> compositeCompletable = CompletableFuture.supplyAsync(() -> {
            System.out.println("first");
            return CompletableFuture.supplyAsync(() -> {
                System.out.println("second");
                return CompletableFuture.supplyAsync(() -> {
                    System.out.println("third");
                    return CompletableFuture.supplyAsync(() -> {
                        System.out.println("fourth");
                        return CompletableFuture.supplyAsync(() -> {
                            System.out.println("fifth");
                            return CompletableFuture.completedFuture(null);
                        });
                    });
                });
            });
        });
        return compositeCompletable;
    }
    
    public static void recursiveGet(Future<?> future) throws InterruptedException, ExecutionException{
        Object result = future.get();
        if(result instanceof Future){
            recursiveGet((Future<?>) result);
        }
    }
    

    返回

    first
    second
    third
    fourth
    fifth
    finished
    

    【讨论】:

      【解决方案3】:

      刚刚测试过,它可以工作。我认为不适合您的原因是因为您在 main 方法中运行并且您没有等待完成。我在你的代码之后做了一个Thread.sleep(1000),它起作用了。最好的方法是等待终止:completableFutureCompletableFuture.get().get().get().get().get()

      【讨论】:

      • @GuyGrin get 只会等待第一个任务完成
      猜你喜欢
      • 1970-01-01
      • 2021-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-04
      • 1970-01-01
      • 2019-10-25
      • 2021-06-03
      相关资源
      最近更新 更多