Тайм-аут с CompletableFuture и CountDownLatch - PullRequest
3 голосов
/ 19 сентября 2019

Я хочу обернуть Runnable в CompletableFuture, который будет вычисляться асинхронно, но с контролем, когда вычисление начинается и заканчивается.Я создал CompletableFuture с CountDownLatch для блокировки обработки, но следующий фрагмент кода выдает ошибку:

CountDownLatch countDownLatch = new CountDownLatch(1);
CompletableFuture completableFuture = CompletableFuture.runAsync(() -> {
    try {
        countDownLatch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("Stop");
});
Thread.sleep(1000L);
System.out.println("Start");
completableFuture.get(1000L, TimeUnit.MILLISECONDS);
countDownLatch.countDown();

Запустить исключение в потоке "main" java.util.concurrent.TimeoutException в java.util.concurrent.CompletableFuture.timedGet (CompletableFuture.java:1771) в java.util.concurrent.CompletableFuture.get (CompletableFuture.java:1915) в Sandbox.main (Sandbox.java:23)

Когда я вызываю get без тайм-аута, с другой стороны, он зависает (печатается только Start).

Я ожидаю, что исполняемый файл в CompletableFuture будет запущен, когда countDownLatch.countDown();называется.

Ответы [ 2 ]

5 голосов
/ 19 сентября 2019

Вы ждете, пока истечет время ожидания, не позволяя потоку продолжить.Future.get блокирует, и это никогда не позволит вам countDown Latch до истечения времени ожидания, следовательно, ваш поток никогда не завершится.Здесь необходимо сначала разрешить потоку вызвать countDown на Latch, а затем подождать с таймаутом в вызове get.Простое обращение двух строк решит проблему.Вот как это выглядит.

countDownLatch.countDown();
completableFuture.get(1000L, TimeUnit.MILLISECONDS);

На самом деле, если вы удалите тайм-аут из вызова get (он блокируется на неопределенный срок), то это типичный пример тупика в системе.Рабочий поток ожидает, пока основной поток не начнет обратный отсчет защелки, в то время как основной поток ожидает завершения рабочего потока, чтобы он мог продолжить и считать защелку.К счастью, истекшее время ожидания позволяет избежать вероятностного тупика.Напротив, вы можете cancel будущее в любое время и избежать потенциальных тупиков, поскольку ваши задачи реагируют на прерывание.

5 голосов
/ 19 сентября 2019

Из-за CompletableFuture#get есть блокирующий вызов.Таким образом, countDownLatch.countDown(); не будет выполняться, пока CompletableFuture#get не получит результат.CompletableFuture не завершится и вернет результат, так как будет ожидать countDownLatch для обратного отсчета.Итак, вы создали зависимость между двумя потоками, так что один будет ждать другого, и наоборот.

...