CompletableFuture和CountDownLatch超时

时间:2019-09-19 16:37:18

标签: java multithreading concurrency java-8 completable-future

我想将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();
  

开始   线程“主”中的异常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中的runnable在调用countDownLatch.countDown();时运行。

2 个答案:

答案 0 :(得分:5)

由于CompletableFuture#get是一个阻止呼叫。因此,countDownLatch.countDown();将在CompletableFuture#get得到结果之前执行。 CompletableFuture不会完成并返回结果,因为它将等待countDownLatch倒数。因此,基本上,您在2个线程之间创建了一个依赖关系,这样一个线程将等待另一个线程,反之亦然。

答案 1 :(得分:5)

您正在等待超时到期,而不允许线程继续进行。 Future.get正在阻塞,并且永远不允许您在超时到期之前countDown Latch,因此您的线程永远不会完成。您要做的是,首先,通过在countDown上调用Latch来使线程继续运行,然后在get调用中等待超时。只需将两条线颠倒即可解决问题。看起来就是这样。

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

实际上,如果您从get调用中删除超时(它无限期地阻塞),那么这是系统中死锁的典型示例。工作线程等待,直到主线程递减计数闩锁为止,而主线程等待工作线程完成,以便继续执行并倒计时锁存器。幸运的是,获得超时的时间可以避免概率性死锁。相反,只要您的任务响应中断,您就可以随时cancel避免将来发生死锁。