Java threading introduction  Thread-safety  Thread methods  Interruption  Thread scheduling  Context switching  Thread priorities  sleep()  yield()  Deadlock  Threading with Swing  invokeLater()  Thread pools  CoundDownLatch  ThreadPoolExecutor  CyclicBarrier

Coordinating threads with CountDownLatch

The CountDownLatch class allows us to coordinate the starting and stopping of threads. Typical uses are as follows:

  • we can make several threads start at the same time;
  • we can wait for several threads to finish (whereas, for example, the Thread.join() method only lets you wait for a single thread).

Introduction to CountDownLatch

In concurrent programming, a latch is a type of "switch" or "trigger". The latch is set up with a particular count value. The count is then counted down, and at strategic moments, a thread or threads waits for the countdown to reach zero before continuing to perform some process. Note that this is a one-off process: once the latch reaches zero, there is no way to reset the count.

In Java:

  • the CountDownLatch object is constructed with the initial count;
  • calling countDown() decrements the count by 1;
  • the await() method will wait for the count to reach zero, or proceed immediately if the count already reached zero.

The CountDownLatch class is designed to be safe to call from multiple threads without any extra synchronization. (This differs, for example, from the wait/notify mechanism, where threads must be synchronized on the given lock object before calling wait() or notify().)

Why use CountDownLatch (rather than wait/notify, Condition etc)?

The CountDownLatch protects you against the case of a thread missing a signal which can occur if you use these other mechanisms for coordinating jobs. Something like a Condition is useful for signalling to threads if they are waiting, but where it doesn't matter if they're not, or where a thread will explicitly check if it has to wait before waiting. With a CountDownLatch, we await a signal if it hasn't been triggered yet, but immediately continue without waiting if that signal was already triggered before we start waiting.

How to make several threads start at the same time

Sometimes it is useful to make a group of threads start at approximately the same time. For example, consider performance tests such as the ones conducted for this web site. If we're testing some throughput with n threads, it's only fair if the n threads start (and stop) at more or less the same time. In another situation, we might want a group of threads to start as soon as some asynchronous initialisation procedure is complete.

To coordinate the starting of several threads, we first create a CountDownLatch with an initial count of 1. Then, each thread will sit at the start of its run() method, waiting for the latch to be counted down (i.e. sitting in the await() method). The thread performing the initialisation step (or just the thread coordinating the start of the other threads in the case of our performance experiment) then calls countDown() on the latch. Because the initial count was 1, this single countdown operation triggers all the other threads to start at (approximately) the same time.

If we define a subclass of Thread to handle the concurrent tasks, then we can arrange to pass the CountDownLatch into the constructor of those threads:

public class LatchedThread extends Thread {
  private final CountDownLatch startLatch;

  public LatchedThread(CountDownLatch startLatch) {
    this.startLatch = startLatch;
  }
  public void run() {
    try {
      startLatch.await();
      // ... perform task
    } catch (InterruptedException iex) {}
  }
}

Then, to coordinate the starting of 4 of these threads:

CountDownLatch startLatch = new CountDownLatch(1);
for (int threadNo = 0; threadNo < 4; threadNo++) {
  Thread t = new LatchedThread(startLatch);
  t.start();
}
// give the threads chance to start up; we could perform
// initialisation code here as well.
Thread.sleep(200);
startLatch.countDown();

When we call countDown() in the main thread, we don't actually know that all of the threads have started up; we just assume that sleeping for a fraction of a second gives them a "reasonable chance" of being ready for the signal. If any of the threads "misses the signal", it won't actually matter too much: when such a thread does start up, enter its run() method await() method, it will no longer actually wait, since the latch has already reached zero.

Bear in mind that inevitably, threads will "wake up" with approximate simultaneity: how simultaneous it can actually be depends on various factors, such as whether each thread can actually be allocated to a free processor, how "busy" the system is (what other threads are running and at what priorities), what threads are doing— i.e. how quickly running threads will relinquish the CPU— and what your particular operating system's policy is on prioritising waiting threads when they are signalled to wake up. (See the section on thread scheduling for more details about these factors.)

How to wait for several threads to complete

Another common scenario is with parallel processing, where we need to wait for several threads to finish or reach a particular point. In this case, we can use a similar mechanism:

  • we construct a CountDownLatch with the number of threads we want to wait for;
  • each thread counts down the latch on termination (or on finishing the job we're interested in).

This is therefore more flexible than the join() method, which only lets us wait for a single thread. Here is an example of waiting for 10 threads to complete:

public class StopLatchedThread extends Thread {
  private final CountDownLatch stopLatch;
  
  public StopLatchedThread(CountDownLatch stopLatch) {
    this.stopLatch = stopLatch;
  }
  public void run() {
    try {
      // perform interesting task
    } finally {
      stopLatch.countDown();
    }
  }
}

public void performParallelTask() throws InterruptedException {
  CountDownLatch cdl = new CountDownLatch(10);
  for (int i = 0; i < 10; i++) {
    Thread t = new StopLatchedThread(cdl);
    t.start();
  }
  cdl.await();
}

Interruptions and timeouts

A thread sitting in the await() method can be interrupted (generally by another thread calling interrupt() on it). Therefore, the await() method throws InterruptedException. Inside a run() method, the most appropriate action is usually to catch the exception around the whole logic of the method, so that interrupting the thread makes it exit. Where we are waiting for threads to complete inside a method, we can just make that method throw the exception up, and let the caller worry about what happens if the process is interrupted. For more information, see the section on thread interruption.

A version of the await() method takes a timeout (and TimeUnit in which the timeout is specified). Setting a timeout could be useful if, for example, the condition that a thread is awaiting is the initialisation of a driver, and there's a chance that the driver will not get initialised in a reasonably amount of time. In the timed case, the method returns true if the latch was actually triggered, and false if a timeout occurred. The timed method can still be interrupted and throw InterruptedException.

Coordinating multi-stage/iterated parallel processes

The CountDownLatch is useful for coordination of one-off operations. In the next section, we look at the CyclicBarrier class, which allows repeated or multi-stage parallel processes to be coordinated.

comments powered by Disqus

 Java threading articles  Java threading and concurrency  Java profiling  Java performance graph index

Unless otherwise stated, the Java programming articles and tutorials on this site are written by Neil Coffey. Suggestions are always welcome if you wish to suggest topics for Java tutorials or programming articles, or if you simply have a programming question that you would like to see answered on this site. Most topics will be considered. But in particular, the site aims to provide tutorials and information on topics that aren't well covered elsewhere, or on Java performance information that is poorly described or understood. Suggestions may be made via the Javamex blog (see the site's front page for details).
Copyright © Javamex UK 2014. All rights reserved.