Reputation: 26046
I want to wrap a Runnable
in CompletableFuture
to be computed asynchronously, but with control over when does the computation begin and end. I've created a CompletableFuture
with CountDownLatch
to block the processing, but the following snippet throws an error:
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)
When I call get
without timeout on the other hand, it freezes (only Start
is printed).
I expect the runnable in CompletableFuture
to run when countDownLatch.countDown();
is called.
Upvotes: 3
Views: 3598
Reputation: 21124
You are waiting till the timeout expires without allowing the thread to proceed. The Future.get
is blocking and that will never allow you to countDown
the Latch
before the timeout expires ever, hence your thread never completes. What you have to do here is, first, let the thread proceed by calling the countDown
on the Latch
and then wait with a timeout in the get call. Just inverting the two lines would solve the issue. Here's how it looks.
countDownLatch.countDown();
completableFuture.get(1000L, TimeUnit.MILLISECONDS);
In fact, if you remove the timeout from the get call (it blocks indefinitely), then this is a typical example of a Deadlock in a system. The worker thread waits until the main thread counts down the latch, while main thread waits for the worker thread to complete so that it can go ahead and countDown the latch. Fortunately, the time out passed to get enables Probabilistic deadlock avoidance. On the contrary, you can cancel
the future at any time and avoid potential deadlocks as far as your tasks are responsive to the interruption.
Upvotes: 5
Reputation: 7315
Because of CompletableFuture#get
is a blocking call. So, countDownLatch.countDown();
will not execute till the time CompletableFuture#get
get the result. CompletableFuture
will not complete and return the result as it will wait to countDownLatch to count down. So, basically you have created a dependency between 2 thread such that one will wait for another and vice-versa.
Upvotes: 4