我想将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();
时运行。
答案 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
避免将来发生死锁。