The JavaTM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form

Trail: Essential Java Classes
Lesson: Doing Two or More Tasks At Once: Threads

## The Producer/Consumer Example

> The `Producer` generates an integer between 0 and 9 (inclusive), stores it in a `CubbyHole` object, and prints the generated number. To make the synchronization problem more interesting, the `Producer` sleeps for a random amount of time between 0 and 100 milliseconds before repeating the number generating cycle:

> >     public class Producer extends Thread {
> >         private CubbyHole cubbyhole;
> >         private int number;

> >         public Producer(CubbyHole c, int number) {
> >             cubbyhole = c;
> >             this.number = number;
> >         }

> >         public void run() {
> >             for (int i = 0; i < 10; i++) {
> >                 cubbyhole.put(i);
> >                 System.out.println("Producer #" + this.number
> >                                    + " put: " + i);
> >                 try {
> >                     sleep((int)(Math.random() * 100));
> >                 } catch (InterruptedException e) { }
> >             }
> >         }
> >     }

> The `Consumer`, being ravenous, consumes all integers from the `CubbyHole` (the exact same object into which the `Producer` put the integers in the first place) as quickly as they become available.

> >     public class Consumer extends Thread {
> >         private CubbyHole cubbyhole;
> >         private int number;

> >         public Consumer(CubbyHole c, int number) {
> >             cubbyhole = c;
> >             this.number = number;
> >         }

> >         public void run() {
> >             int value = 0;
> >             for (int i = 0; i < 10; i++) {
> >                 value = cubbyhole.get();
> >                 System.out.println("Consumer #" + this.number
> >                                    + " got: " + value);
> >             }
> >         }
> >     }

> The `Producer` and `Consumer` in this example share data through a common `CubbyHole` object. And you will note that neither the `Producer` nor the `Consumer` makes any effort whatsoever to ensure that the `Consumer` is getting each value produced once and only once. The synchronization between these two threads actually occurs at a lower level, within the `get` and `put` methods of the `CubbyHole` object. However, let's assume for a moment that these two threads make no arrangements for synchronization and talk about the potential problems that might arise in that situation.

> One problem arises when the `Producer` is quicker than the `Consumer` and generates two numbers before the `Consumer` has a chance to consume the first one. Thus the `Consumer` would skip a number. Part of the output might look like this:

> >         . . .

> >     Consumer #1 got: 3
> >     Producer #1 put: 4
> >     Producer #1 put: 5
> >     Consumer #1 got: 5

> >         . . .

> Another problem that might arise is when the `Consumer` is quicker than the `Producer` and consumes the same value twice. In this situation, the `Consumer` would print the same value twice and might produce output that looked like this:

> >         . . .

> >     Producer #1 put: 4
> >     Consumer #1 got: 4
> >     Consumer #1 got: 4
> >     Producer #1 put: 5

> >         . . .

> Either way, the result is wrong. You want the `Consumer` to get each integer produced by the `Producer` exactly once. Problems such as those just described are called race conditions. They arise from multiple, asynchronously executing threads trying to access a single object at the same time and getting the wrong result.

> Race conditions in the producer/consumer example are prevented by having the storage of a new integer into the `CubbyHole` by the `Producer` be synchronized with the retrieval of an integer from the `CubbyHole` by the `Consumer`. The `Consumer` must consume each integer exactly once.

> The activities of the `Producer` and `Consumer` must be synchronized in two ways. First, the two threads must not simultaneously access the `CubbyHole`. A Java thread can prevent this from happening by locking an object. When an object is locked by one thread and another thread tries to call a synchronized method on the same object, the second thread will block until the object is unlocked. Locking an Object discusses this.

> And second, the two threads must do some simple coordination. That is, the `Producer` must have some way to indicate to the `Consumer` that the value is ready and the `Consumer` must have some way to indicate that the value has been retrieved. The `Thread` class provides a collection of methods--`wait`, `notify`, and `notifyAll`--to help threads wait for a condition and notify other threads of when that condition changes. Using the `notifyAll` and `wait` Methods has more information.

### The Main Program

> Here's a small stand-alone `Java application` that creates a `CubbyHole` object, a `Producer`, a `Consumer`, and then starts both the `Producer` and the `Consumer`.

> >     public class ProducerConsumerTest {
> >         public static void main(String[] args) {
> >             CubbyHole c = new CubbyHole();
> >             Producer p1 = new Producer(c, 1);
> >             Consumer c1 = new Consumer(c, 1);

> >             p1.start();
> >             c1.start();
> >         }
> >     }

### The Output

> Here's the output of ProducerConsumerTest.

> >     Producer #1 put: 0
> >     Consumer #1 got: 0
> >     Producer #1 put: 1
> >     Consumer #1 got: 1
> >     Producer #1 put: 2
> >     Consumer #1 got: 2
> >     Producer #1 put: 3
> >     Consumer #1 got: 3
> >     Producer #1 put: 4
> >     Consumer #1 got: 4
> >     Producer #1 put: 5
> >     Consumer #1 got: 5
> >     Producer #1 put: 6
> >     Consumer #1 got: 6
> >     Producer #1 put: 7
> >     Consumer #1 got: 7
> >     Producer #1 put: 8
> >     Consumer #1 got: 8
> >     Producer #1 put: 9
> >     Consumer #1 got: 9

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form