A monitor is a combination of a mutex (lock) and one or more condition variables. Support for monitors in Java is integrated with the language. This is a good thing because it is syntactically clean and it defends against various common errors. However, it is somewhat constraining. 1. Java supports mutex acquire/release via the synchronized keyword: a method or a code block within a method may be declared as synchronized. The mutex is acquired (locked) on entry to the synchronized block or method, and released (unlocked) on exit from the synchronized block or method. This approach is clean and safe because it is impossible to "forget to unlock" or to try to acquire the same lock twice. However, it is constraining in that you can't acquire a mutex in one method and then release it in another method. 2. Java monitors are integrated into every object: each object is or has a monitor. You can make a synchronized block on any object by reference: if no reference is specified, then the default is "this" (the current object). This approach is clean and safe, because it is not necessary to declare the association of condition variables with their mutexes, and it is often not necessary to even declare which mutex or condition variable is being used. It is safe because it is impossible to use the wrong mutex for a condition variable. But it is constraining: each Java monitor has only a single condition variable. This state of affairs suggests an instructive synchronization problem: can we use Java monitors to implement a more general monitor class? Let us call this class Monitor. The idea is that a program can allocate a Monitor object (using new), and then use the Monitor in the classic way via an API with the five classic methods: acquire, release, wait, signal, and broadcast. Moreover, a Monitor can have multiple condition variables. To avoid confusion let's refer to them as "wait channels". Let us name them by integers (channel IDs) to make them easy to use and implement. If a thread waits on a channel ID, it is awakened only by a signal or broadcast on the same channel ID. Because a Monitor object is a Java object, it has its own Java monitor. The problem is to use a Monitor object's internal monitor---and nothing more---to implement synchronization for the Monitor's five methods: acquire, release, waitOnChannel, signalOnChannel, and broadcastOnChannel. It is easy to implement acquire and release. Each Monitor needs a boolean variable that says whether or not the lock is free or held. Acquire waits until the lock is free, and then sets it as held. Release sets the lock as free, and notifies a waiter. synchronized void acquire() { while (held) wait(); held = true; /* mine! */ } synchronized void release() { held = false; notify(); } This is a good example of how to use synchronization in Java. The methods are synchronized, which means that they lock the current object's internal mutex on entry and release it on exit. The wait and notify are on the current object's internal condition variable. Note that a program may use lots of Monitor objects, and they won't interfere with one another because each one has its own internal mutex and condition variable. It should be obvious why this code is safe from races. The object's internal mutex protects the held flag: any thread executing in these synchronized methods can be sure that the held flag does not change unless that thread itself changes it. Be sure to understand why the while loop is needed in acquire. Java uses Mesa semantics, and monitors with Mesa semantics require that a waking waiter must check to see if some other thread has gotten in first and changed the condition again before it has had a chance to run. A waking waiter acquires the mutex before returning from wait back into the synchronized block. Some other thread may beat it to the mutex. In this case, some other thread may complete an acquire and set the held flag again before a waiter awakened by a notify has a chance to run. The while loop defends against this by double-checking that the Monitor really is free before dropping out of the wait and grabbing it. This is "looping before leaping". Note also that a Monitor implicitly has a queue of threads waiting on the mutex and a queue of threads waiting for each of its channels. However, it is almost never necessary to implement thread queues for a synchronization problem, because Java's monitors already have them under the hood. In this case we do not need to keep a queue of threads waiting on the Monitor's lock: instead we let the queue of threads on the object's internal condition variable keep track of them. We just wait. We can use the same queue to implement the implicit thread queues for each wait channel in the Monitor. We can do this easily by putting all waiting threads to sleep on the same condition variable---the object's internal condition variable---and using Java's notifyAll (broadcast) to wake them all up. Thundering herds make this solution inefficient if there are lots of waiting threads, but it is clean, simple, and correct. If we use that approach, then all the waking waiters must check for themselves if the notifyAll applies to them, and go back to sleep if it does not. We use a while loop for that purpose: yet another reason to always loop before leaping. synchronized void waitOnChannel(int cid) { release(); while (it's not this waiter's turn to awaken yet) wait(); acquire(); } synchronized void signalOnChannel(int cid) { say who should awaken; notifyAll(); } But what is the condition/predicate in the while statement? How should signalOnChannel say who should awaken? It is a little tricky to implement the state logic. One solution is to keep two counters for each channel: a count of the total number of waits (waiters), and a count of the total number of waiters awakened by a signal or broadcast (awakenings). This is a common trick sometimes called "sequencers". As a mildly pleasant side effect it will make our Monitors fair and FIFO, whatever the scheduling policy of the underlying Java implementation. We can keep the counters in an array indexed by channel ID. This array could be uncomfortably big and sparse. A real implementation could use a hash table instead. But let's not concern ourselves with this implementation detail, and instead just focus on the synchronization. synchronized void waitOnChannel(int cid) { release(); waiters[cid]++; int myWaiterNumber = waiters[cid]; while (awakenings[cid] < myWaiterNumber) wait(); acquire(); } synchronized void signalOnChannel(int cid) { if (awakenings[cid] == waiters[cid]) /* no waiters waiting on this channel */ return; awakenings[cid]++; /* awaken one waiter on this channel */ notifyAll(); } synchronized void broadcastOnChannel(int cid) { awakenings[cid] = waiters[cid]; /* awaken all waiters on this channel */ notifyAll(); } Sequencers are like the "please take a number" and "now serving" signs at a bakery or at the DMV. A waiting thread takes a number and waits until its number is called. Signal calls the next number. Broadcast calls the numbers for all waiters.