Waiting for Another Thread With CountDownLatch
See how you make the best use of CountDownLatch to wait for threads with these two real-life examples regarding initialization and testing multithreaded software.
Join the DZone community and get the full member experience.
Join For FreeCountDownLatch is an easy way to wait until another thread has finished a task. The CountDownLatch is initialized by the number of threads we need to wait for. After a thread has finished its task, it calls countDown, counting down the countdown. A thread can wait until the countdown reaches zero by calling await. We will see how to use it by looking at two real-life examples.
How to Use CountDownLatch: Waiting for Initialization
The Apache Ignite CacheDataStructuresManager needs to wait until it is initialized. One thread initializes the CacheDataStructuresManager, so we need a CountDownLatch with count one:
private final CountDownLatch initLatch = new CountDownLatch(1);
The initialization is done in the onKernalStart0() method:
@Override protected void onKernalStart0()
throws IgniteCheckedException {
try {
queueHdrView = cctx.cache();
initFlag = true;
}
finally {
initLatch.countDown();
}
}
The countDown method is called in the finally block, so we guarantee that the countdown eventually reaches zero and we do not wait forever. To signal that the initialization was successful, we use the boolean flag initFlag.
The method waitInitialization is used to check if the initialization was successful:
private void waitInitialization()
throws IgniteCheckedException {
if (initLatch.getCount() > 0)
U.await(initLatch);
if (!initFlag)
throw new IgniteCheckedException(
"DataStructures manager was not properly initialized.");
}
We check the count of the CountDownLatch, waiting if necessary in the await method from IgniteUtils. After that, we check the initFlag if the initialization was successful.
The method await from IgniteUtils handles the InterruptedException from the CountDownLatch await method:
public static void await(CountDownLatch latch)
throws IgniteInterruptedCheckedException {
try {
if (latch.getCount() > 0)
latch.await();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IgniteInterruptedCheckedException(e);
}
}
In the example, the interrupt flag is restored by calling Thread.currentThread().interrupt() and a new checked Exception is thrown.
Handling of InterruptedException
The await method of the CountDownLatch throws an InterruptedException when the method Thread.interrupt was called. The CountDownLatch clears the interrupt flag when it throws the InterruptedException. Therefore, the book Java Concurrency in Practice - Brian Goetz, et al. recommends to either propagate the InterruptedException or to restore the interrupt flag.
The Guava library provides the utility method, awaitUninterruptibly, in the class Uninterruptibles, which implements the second recommendation. It restores the interrupt flag. Using this method, you make sure that another blocking method will again throw an InterruptedException.
How to Use Await With a Timeout: Testing Multithreaded Software
The next example shows how to call the second method to wait for other threads, await with a timeout: This method is typically used in tests, like in this example in the PostServletTest from jetty:
@Test
public void testBadPost() throws Exception
{
StringBuilder req = new StringBuilder(16*1024);
// creation of the request String omitted
String resp = connector.getResponse(req.toString());
assertThat(resp,startsWith("HTTP/1.1 200 OK"));
assertTrue(complete.await(5,TimeUnit.SECONDS));
assertThat(ex0.get(),not(nullValue()));
assertThat(ex1.get(),not(nullValue()));
}
The await method gets called with the amount we want to wait, together with the unit for the amount, line 8. In our example, we want to wait no more than 5 seconds. The test fails when the await method returns false because of the timeout.
Summary and Next Steps
When you want to wait for another thread with CountDownLatch follow these steps:
- Initialize the CountDownLatch with the number of threads you are waiting for.
- Call the countDown method in the finally block when a thread has finished its task.
- Use a flag or counter to signal that the tasks were successful.
- Wait for the threads with the method await. Propagate the InterruptedException thrown by await or restore the interrupted flag when you catch the InterruptedException.
The CountDownLatch can only be used once. We will look at the CyclicBarrier next, which can be used multiple times. I would be glad to hear from you about how you use CountDownLatch in your application.
Published at DZone with permission of Thomas Krieger, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments