March 21, 2018
Category: The package java.util.concurrent
CountDownLatch is an easy way to wait till 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 till the countdown reaches zero by calling await. We will see how to use it by looking at two real-life examples.
The apache ignite CacheDataStructuresManager needs to wait till 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.
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.
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.
Java provides three classes to wait for other threads: CountDownLatch, CyclicBarrier, and Phaser. When you need to wait for tasks done in other threads use CountDownLatch. Use CyclicBarrier when you do the work and need to wait in the same threads. To use CyclicBarrier or CountDownLatch you need to know the number of threads when you call the constructor. If you need to add threads after construction time, use the class Phaser.
When you want to wait for another thread with CountDownLatch follow these steps:
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.
© 2020 vmlens Legal Notice Privacy Policy