Previous | Next | Trail Map | Writing Java Programs | Threads of Control


Monitors

The Java language and runtime system support thread synchronization through the use of monitors which were first outlined in C. A. R. Hoare's article Communicating Sequential Processes (Communications of the ACM, Vol. 21, No. 8, August 1978, pp. 666-677). In general, a monitor is associated with a specific data item (the condition variable) and functions as a lock on that data. When a thread holds the monitor for some data item, other threads are locked out and cannot inspect or modify the data.

The code segments within a program that make it possible for separate, concurrent threads to access the same data items are known as critical sections. In the Java language, you identify critical sections in your program with the synchronized keyword.
Note: Generally, critical sections in Java programs are methods. You can mark smaller code segments as synchronized. However, this violates object-oriented paradigms and leads to confusing code that is difficult to debug and maintain. For the majority of your Java programming purposes, it's best to use synchronized only at the method level.

In the Java language, a unique monitor is associated with every object that has a synchronized method. The CubbyHole class for the Producer/Consumer example introduced on the previous page has two synchronized methods: the put() method used to change the value in the CubbyHole and the get() method used to retrieve the current value. Thus the system associates a unique monitor with every instance of CubbyHole.

class CubbyHole {
    private int seq;
    private boolean available = false;

    public synchronized int get() {
        while (available == false) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        available = false;
        notify();
        return seq;
    }

    public synchronized void put(int value) {
        while (available == true) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        seq = value;
        available = true;
        notify();
    }
}
The CubbyHole has two private variables: seq which is the current contents of the CubbyHole, and the boolean variable available which indicates whether the CubbyHole contents can be retrieved. When available is true the Producer has just put a new value in the CubbyHole and the Consumer has not yet consumed it. The Consumer can only consume the value in the CubbyHole when available is true.

The Java language provides a unique monitor for each instance of CubbyHole (including the one shared by the Producer and the Consumer) because CubbyHole has synchronized methods. Whenever control enters a synchronized method, the thread that called the method acquires the monitor for the object whose method has been called. Now, other threads cannot call a synchronized method on the same object until the monitor is released.


Note: Java Monitors are Re-entrant. That is, the same thread can call a synchronized method on an object for which it already holds the monitor, thereby re-acquiring the monitor.

Thus, whenever the Producer calls the CubbyHole's put() method, the Producer acquires the monitor for the CubbyHole thereby preventing the Consumer from calling the CubbyHole's get() method.

public synchronized void put(int value) {
        // monitor has been acquired by the Producer
    while (available == true) {
        try {
            wait();
        } catch (InterruptedException e) {
        }
    }
    seq = value;
    available = true;
    notify();
        // monitor is released by the Producer
}
When the put() method returns, the Producer releases the monitor thereby unlocking the CubbyHole.

Conversely, whenever the Consumer calls the CubbyHole's get() method, the Consumer acquires the monitor for the CubbyHole thereby preventing the Producer from calling the put() method.

public synchronized int get() {
        // monitor has been acquired by the Consumer
    while (available == false) {
        try {
            wait();
        } catch (InterruptedException e) {
        }
    }
    available = false;
    notify();
    return seq;
        // monitor is released by the Consumer
}
The acquisition and release of a monitor is done automatically and atomically by the Java runtime system. This ensures that race conditions cannot occur in the underlying implementation of the threads and ensures data integrity.


Previous | Next | Trail Map | Writing Java Programs | Threads of Control