2015-06-16 3 views
4

Вот MCVE:Когда завершено? На самом деле завершено?

public static void main(String[] args) { 
    CompletableFuture<String> r1 = CompletableFuture.supplyAsync(() -> { 
     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException e) { 
      throw new RuntimeException(e); 
     } 
     return "41"; 
    }); 
    CompletableFuture<String> r2 = CompletableFuture.supplyAsync(() -> "42"); 
    CompletableFuture<String> r3 = CompletableFuture.supplyAsync(() -> { 
     System.out.println("I'm called."); 
     return "43"; 
    }); 
    CompletableFuture.allOf(r1, r2, r3).thenRun(() -> { System.out.println("End."); }); 
    Stream.of(r1, r2, r3).forEach(System.out::println); 
} 

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

I'm called. 
java[email protected]<...>[Not completed, 1 dependents] 
[email protected]<...>[Completed normally] 
[email protected]<...>[Completed normally] 

Могу ли я знать, что вызывает виртуальная машина для лечения/думаю, что r1 имеет 1 (estimated number of) dependent CompletableFuture, в то время как он решает прямолинейно полный r2 и r3? Единственное различие, которое я вижу, это просто try-catch, так и ответ такой же простой?

Для сравнения, я получаю ожидаемое время ожидания 5 секунд и следующий вывод, когда я на самом деле делаю join() в конце. Если это помогает, я сталкиваюсь с этим на Java 8 Update 40 JVM.

Модификация:

// ... 
CompletableFuture.allOf(r1, r2, r3).thenRun(() -> { System.out.println("End."); }).join(); 
Stream.of(r1, r2, r3).forEach(System.out::println); 

Выход:

I'm called. 
// <note: 5-second wait is here> 
End. 
[email protected]<...>[Completed normally] 
[email protected]<...>[Completed normally] 
[email protected]<...>[Completed normally] 
+2

«он решает прямо закончить r2 и r3» - никоим образом; это просто, что они завершены так быстро, до вызова 'forEach (:: println)'. – ZhongYu

+0

@bayou println не заботится, если они завершены или вы имеете в виду что-то еще? –

+0

Я имею в виду, что когда 'println (r2)' выполняется, 'r2' заканчивается исполнителем. это, конечно, не гарантировано. – ZhongYu

ответ

4

r1 и r2 являются CompletableFuture s для двух независимо представленных асинхронных задач.

Могу ли я узнать, что вызывает виртуальная машина для лечения/думаю, что r1 имеет 1 (предполагаемое количество) зависимой CompletableFuture, в то время как он решает прямолинейно полной r2 и r3

Это не , Когда вы назовете println на этих экземплярах, r1 и r2 завершили нормально (они не очень много). r1 не имеет (нить, которая бы завершилась, скорее всего, сон).

Звонок allOf не блокирует. Он вернет собственный CompletableFuture, который будет завершен, когда все CompletableFuture, которые вы им дали, выполнены. Вы связываете это с другим CompletableFuture с thenRun, который, начиная с r2 и r3, просто зависит от r1, т.е. он завершается, когда завершается r1.

Вы решили отказаться от ссылки на это CompletableFuture, но задача, отправленная на thenRun, запланирована. Если добавить

Thread.sleep(6000); 

в конце исходной программы, вы увидите ваш журнал End. распечатывается, когда r1 завершается и, следовательно, один возвращаемый thenRun.


Обратите внимание, что, если не указано иное, ваши задачи асинхронных внутри CompletableFuture (например, поданные через supplyAsync) все побежали в по умолчанию ForkJoinPool который использует демон темы. Ваше приложение будет завершено до истечения 5 секунд, если вы не захотите что-то заблокировать и дождитесь, пока это пройдет.

+0

* «Ваше приложение выйдет до истечения 5 секунд, если вы не захотите что-то заблокировать и дождитесь, пока это время пройдет» *. Спасибо, что упомянул очевидное, я не смог сам это понять. : D –

+3

Для полноты обратите внимание, что вы можете подождать специально для выполнения задач, переданных в общий пул, вызывая 'ForkJoinPool.commonPool(). AwaitQuiescence (...)'. – glts