2017-02-20 8 views
2

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

Любое объяснение (и пример того, как исправить) был бы весьма признателен

+1

Вы, вероятно, не хотят иметь вложенные фьючерсы, как это. Вы должны посмотреть ['thenCompose [Async]()'] (https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#thenCompose-java.util .function.Function-). –

ответ

1

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

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

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

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

... тогда вы принудительно завершаете все фьючерсы, и все работает так, как ожидалось.

+0

Спасибо. Это сработало. –

2

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

+0

спасибо. См. Мое редактирование. Проблема сохраняется. –

+0

@GuyGrin get будет ждать только для завершения первой задачи – user1121883

+0

спасибо. Это сработало. –

0

Это происходит потому, что ваш CompletableFuture выполняется асинхронно, но ваша программа завершается до того, как произойдет пятый вызов (я предполагаю, что вы запустили его в одном главном и вернули сразу после создания вашего фьючерса).

Как вы можете не знать, сколько Будущих укладывается в ваше будущее (из-за стирания типа). Вы можете выполнить рекурсивный .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