Вложенные фьючерсы не выполняются

Я столкнулся со странной ситуацией. Я возился с 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, которые не будут выполняться.

Буду очень признателен за любое объяснение (и пример того, как исправить)


person Guy Grin    schedule 20.02.2017    source источник
comment
Вы, вероятно, не хотите иметь такие вложенные фьючерсы. Вам следует взглянуть на thenCompose[Async]().   -  person Didier L    schedule 21.02.2017


Ответы (3)


Причина, по которой это не работает, заключается в том, что в вашем простом тесте виртуальная машина завершает работу до завершения всех задач.

Когда вы вызываете completableFutureCompletableFuture.get(), гарантируется завершение только первого вложения фьючерсов. Виртуальная машина завершает работу, и все потоки уничтожаются.

Другими словами, первое вложенное будущее все еще может быть «незавершенным», поскольку его поток все еще может быть занят. Однако, когда вы пытаетесь получить результат с помощью get, он, конечно, дождется завершения и будет работать, как ожидалось. Просто попробуй:

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

... затем вы заставляете все фьючерсы завершиться, и все работает так, как ожидалось.

person john16384    schedule 20.02.2017

Только что проверил это, и это работает. Я думаю, что причина, по которой у вас не работает, заключается в том, что вы запустили основной метод и не дождались завершения. Я сделал Thread.sleep(1000) после вашего кода, и это сработало. Лучше всего было бы дождаться прекращения: completableFutureCompletableFuture.get().get().get().get().get()

person user1121883    schedule 20.02.2017
comment
@GuyGrin get будет ждать только завершения первой задачи - person user1121883; 20.02.2017

Это происходит потому, что ваш 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
person Jeremy Grand    schedule 20.02.2017