CompletableFuture 和 CountDownLatch 超时
Timeout with CompletableFuture and CountDownLatch
我想在 CompletableFuture
中包装一个 Runnable
以进行异步计算,但可以控制计算何时开始和结束。我创建了一个 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();
Start
Exception in thread "main" java.util.concurrent.TimeoutException
at java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1771)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1915)
at Sandbox.main(Sandbox.java:23)
另一方面,当我在没有超时的情况下调用 get
时,它会冻结(只打印 Start
)。
我希望 CompletableFuture
中的 运行 能够在调用 countDownLatch.countDown();
时变为 运行。
因为CompletableFuture#get
是阻塞调用。所以,countDownLatch.countDown();
不会执行,直到CompletableFuture#get
得到结果。 CompletableFuture
不会完成,return 结果会等待 countDownLatch 倒计时。所以,基本上你已经在 2 个线程之间创建了一个依赖关系,这样一个线程将等待另一个线程,反之亦然。
您正在等待直到超时到期而不允许线程继续。 Future.get
正在阻塞,并且永远不会允许您在超时到期之前 countDown
Latch
,因此您的线程永远不会完成。您在这里要做的是,首先,通过在 Latch
上调用 countDown
让线程继续,然后在 get 调用中等待超时。只需反转两条线即可解决问题。这是它的样子。
countDownLatch.countDown();
completableFuture.get(1000L, TimeUnit.MILLISECONDS);
事实上,如果您从 get 调用中删除超时(它无限期地阻塞),那么这是系统中死锁的典型示例。工作线程一直等到主线程对闩锁进行倒计时,而主线程等待工作线程完成,以便它可以继续对闩锁进行倒计时。幸运的是,传递给 get 的超时启用了概率死锁避免。相反,您可以随时 cancel
未来并避免潜在的死锁,只要您的任务能够响应中断。
我想在 CompletableFuture
中包装一个 Runnable
以进行异步计算,但可以控制计算何时开始和结束。我创建了一个 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();
Start Exception in thread "main" java.util.concurrent.TimeoutException at java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1771) at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1915) at Sandbox.main(Sandbox.java:23)
另一方面,当我在没有超时的情况下调用 get
时,它会冻结(只打印 Start
)。
我希望 CompletableFuture
中的 运行 能够在调用 countDownLatch.countDown();
时变为 运行。
因为CompletableFuture#get
是阻塞调用。所以,countDownLatch.countDown();
不会执行,直到CompletableFuture#get
得到结果。 CompletableFuture
不会完成,return 结果会等待 countDownLatch 倒计时。所以,基本上你已经在 2 个线程之间创建了一个依赖关系,这样一个线程将等待另一个线程,反之亦然。
您正在等待直到超时到期而不允许线程继续。 Future.get
正在阻塞,并且永远不会允许您在超时到期之前 countDown
Latch
,因此您的线程永远不会完成。您在这里要做的是,首先,通过在 Latch
上调用 countDown
让线程继续,然后在 get 调用中等待超时。只需反转两条线即可解决问题。这是它的样子。
countDownLatch.countDown();
completableFuture.get(1000L, TimeUnit.MILLISECONDS);
事实上,如果您从 get 调用中删除超时(它无限期地阻塞),那么这是系统中死锁的典型示例。工作线程一直等到主线程对闩锁进行倒计时,而主线程等待工作线程完成,以便它可以继续对闩锁进行倒计时。幸运的是,传递给 get 的超时启用了概率死锁避免。相反,您可以随时 cancel
未来并避免潜在的死锁,只要您的任务能够响应中断。