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

The producer-consumer pattern in Java 5: using blocking queues in preference to wait()/notify()

A common use for the wait/notify mechanism is to implement what is sometimes called a producer-consumer pattern. What is meant by this is that one thread "produces" work that another thread, or various other threads, then carry out at a convenient moment. Examples of this pattern include:

  • a messaging thread logs messages "passed" to it from other threads;
  • worker threads of a web server "notify" a statistics thread to update some central statistics on each request.

A typical case for using the pattern is thus to separate tasks with different priorities. Logging, for example, can be a relatively expensive operation and we may not want it to delay completing another operation. By delegating logging to another thread, we can effectively allow logging to take place at a future moment when "there's nothing better to do".

The producer-consumer pattern works by having some queue of pending tasks. The producer places tasks in the list; the consumer removes them. Both parties use suitable synchronization.

Producer-consumer before Java 5: using a List with wait/notify

Pre Java 5, the common way to implement a producer-consumer pattern was to use a plain old LinkedList with explicit synchronization. When we add a "job" to the list, we call notify(); in another thread, the consumer is sitting waiting for the job to come in. So the code would look something like this:

public class LoggingThread extends Thread {
  private LinkedList linesToLog = new LinkedList();
  private volatile boolean terminateRequested;

  public void run() {
    try {
      while (!terminateRequested) {
        String line;
        synchronized (linesToLog) {
          while (linesToLog.isEmpty())
            linesToLog.wait();
          line = (String) linesToLog.removeFirst();
        }
        doLogLine(line);
      }
    } catch (InterruptedException ex) {
      Thread.currentThread().interrupt();
    }
  }

  private void doLogLine(String line) {
    // ... write to wherever
  }

  public void log(String line) {
    synchronized (linesToLog) {
      linesToLog.add(line);
      linesToLog.notify();
    }
  }
}

The code is a little messy because we have no explicit queue object: we just use an everyday list with code around it to perform the queuing. The queuing code might get more complex, for example, if we wanted to limit the number of items that could be queued, or if we wanted to prioritise items in the queue rather than having a simple first-in-first-out policy. The wait/notify mechanism also provides us no means of imposing fairness: that is, if two threads want to add a logging while the list is locked (because a line is being logged from it), which line gets logged first is essentially random. In the case of logging, this may not seem such a big deal (though in rare debugging cases could complicate things if you don't know "what happened first"). But in other cases it could matter more.

The Java 5 producer-consumer pattern

Java 5 improves the producer-consumer pattern by providing explicit blocking queue classes. A blocking queue effectively takes the place of the list in the code above, and also handles the associated synchronization, waiting and notifying (though under the hood, these new classes use the Java 5 lock features rather than a "raw" wait/notify).

On the next page, we continue by looking at Java blocking queues.

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.