Timeout with CompletableFuture and CountDownLatch

And*_*cus 3 java concurrency multithreading java-8 completable-future

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();
Run Code Online (Sandbox Code Playgroud)

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.

Ami*_*era 5

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.


Rav*_*ala 5

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);
Run Code Online (Sandbox Code Playgroud)

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.