Home  Synchronization and concurrency  wait/notify  final  volatile  synchronized keyword  Java threading  Deadlock (and avoiding it)  Java 5: ConcurrentHashMap  Atomic variables  Explicit locks  Queues  Semaphores  CountDownLatch  CyclicBarrier

Using wait/notify to coordinate threads (latch)

Pior to Java 5 at least, one use for the wait-notify mechanism was to coordinate threads performing a particular multithreaded job. We can use wait() and notify() to implement something called a latch: effectively a counter that triggers an event when it reaches zero. The idea is as follows:

  • The "master thread" initialises a counter to the number of job threads;
  • The master thread then starts the job threads;
  • The master thread calls wait() on an object that will be notified when the counter reaches zero;
  • At the end of each job thread's execution, that thread decrements the counter and calls notify() if the counter has reached zero.

We can separate out the counter or latch into a separate component and implement it something like this:

public class Latch {
  private final Object synchObj = new Object();
  private int count;

  public Latch(int noThreads) {
    synchronized (synchObj) {
      this.count = noThreads;
    }
  }
  public void awaitZero() throws InterruptedException {
    synchronized (synchObj) {
      while (count > 0) {
        synchObj.wait();
      }
    }
  }
  public void countDown() {
    synchronized (synchObj) {
      if (--count <= 0) {
        synchObj.notifyAll();
      }
    }
  }
}

Note that this implementation has the things we mentioned in our introduction to wait/notify: we must synchronize on the object that we are going to call wait() and notify() on (we actually use an internal object created for that purpose: this is to hide the internals of the latch from outside callers), and we must call wait() inside a loop. Subtly, we must also synchronize on this "synch obj" variable when we set the initial count inside the constructor. (This is just a specific case of the generality that if we're reading and writing a variable in different threads, we must provide some form of synchronization around all accesses to that variable.) Inside countDown(), a strict implementation might decide to throw an IllegalStateException if the counter was already zero, but we'll keep things simple here.

Using our latch class to coordinate threads

Now let's say we want to coordinate some threads running in parallel (for example, to parallelise a loop). We do so as follows:

Latch l = new Latch(noThreads);
for (int i = 0; i < noThreads; i++) {
  Thread j = new JobSlice(l, ...);
  j.start();
}
l.awaitZero();

using a definition of JobSlice something like this (we'd pass other parameters to the constructor, defining which portion of the loop or job the individual thread was to perform):

class JobSlice extends Thread {
  private Latch latch;
  public JobSlice(Latch l, ...) {
    this.latch = l;
  }
  public void run() {
    try {
      // do calculation
    } finally {
      latch.countDown();
    }
  }
}

Note that we don't deal fully with error handling here. Putting the count down in a finally means that the controller thread won't wait forever in the case of an error, but it would probably need to query whether or not the job threads all completed successfully. One option would be to add a raiseError() method to the latch, which could be called by the job threads and which would cause awaitZero() to throw an error (inside raiseError(), set the count to zero, but also set a boolean variable indicating that an error occurred; inside awaitZero(), check this variable after the wait loop and then either return normally or raise the exception).

CountDownLatch in Java 5

As of Java 5, we don't need to invent our own latch class to do simple thread coordination such as this: the java.util.concurrent package provides an implementation in the form of CountDownLatch. As in our example above, CountDownLatch is initialised with the number of calls to the countDown() method required before the waiter is allowed to proceed. In this case, the method called by the waiter is simply called await(); it is also possible to call await() with a maximum wait time (this version returns a boolean to indicate whether or not the latched actually reached zero).

It is important not to muddle up await() and wait() on CountDownLatch. The wait() method is of course part of the wait-notify mechanism just as for any object (on a latch object, you would basically never use this but Java provides no means to remove these methods from an object).

When would you use CountDownLatch?

At first glance, this may seem like some quirky functionality that we won't use very often. However, the problem of coordinating threads in this way is likely to become very important in the near future, as ordinary desktop computers become increasingly multiprocessors. We can no longer write a task in a single-threaded loop and expect it to magically get faster with newer computers. To take advantage of newer machines' capabilities, even fairly mundane problems will need to be broken down into parallel jobs and could be controlled in a similar way to the method outlined here.

So a short answer is: wherever you can run parallel threads to collectively complete a given task, and those threads will start and stop at roughly the same time; a typical case will be parallelising loops.

Article written by Neil Coffey (@BitterCoffey).

Software

 LetterMeister (word puzzle game for iPhone)
 Currency Quoter (currency converter/predictor)
 French Vocab Games for iPhone/iPad
 Vocabularium: create Spanish vocab podcasts


Java programming articles and tutorials on this site are written by Neil Coffey (@BitterCoffey). 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 © Neil Coffey 2014. All rights reserved.