What is the historical context of Java's thread model? For example, what's a monitor?

Terence Parr

Java programmers sometimes hear that Java's mutual exclusion mechanism is based upon monitors. Here, I give the historical context for Java's thread model and define monitor.

In the mid-1960's, E. Dijkstra invented the notion of a semaphore for implementing mutual exclusion and signaling events and defined P and V operations to specify critical sections. Unfortunately, semaphores are very low-level elements and programs written with them are hard to read as both condition synchronization and mutual exclusion are specified with the same mechanism. In the early 1970's, Tony Hoare defined monitors, which provided a structured approach to exclusion and condition synchronization. Monitors were eventually included in Concurrent Pascal, Modula, and Mesa (at Xerox PARC) [And91]. A number of the PARC researchers that worked on the Cedar/Mesa project now work at JavaSoft. Naturally, the Java thread synchronization mechanism, which is monitor-based, evolved from Cedar/Mesa.

A monitor is chunk of data that can only be accessed through a set of routines. This type of encapsulation is expressed as an object in today's terminology, although monitors were designed like modules rather than instances of a class. A monitor's access routines are guaranteed to execute in a mutually exclusive manner. Java relaxes this constraint slightly to allow a class' methods to be explicitly specified as synchronized, which always execute to completion.

Monitors use condition variables plus wait and signal statements to provide condition synchronization. An accessor routine waits on a condition variable until awakened by another thread executing a signal statement on that variable. Java has a simpler, more efficient, condition synchronization scheme. A thread executes a wait() in a method of some object and blocks until awakened. A call to notifyAll() awakens all threads waiting on a signal for that object--there is no way to specify that only certain threads are to be awakened (a call to notify() wakes up a single waiting thread).

One can say that a monitor access routine acquires a lock on that monitor, which it releases upon returning from that method. In this way, mutual exclusion is implicitly achieved. Only synchronized Java methods acquire a lock on an object. A call to wait() releases the lock, but will reacquire it as it is awakened by a notifyAll(). notifyAll() does not yield execution nor release its hold on an object's lock. The only way to release a lock is by waiting or returning from a synchronized method.

As a final detail, note that some operations are atomic and, hence, do not require synchronization of any kind. In Java, only assignments to primitives except long and double are considered atomic.

[And91] Concurrent Programming: Principles and Practice by Gregory Andrews, Addison-Wesley, Menlo Park, CA, 1991.