next up previous
Next: 3.2.1 Deadlock Up: 3.2 What is Concurrent Previous: 3.2 What is Concurrent

3.2.0.1 Synchronized methods and statements

We've already discussed synchronized methods above. We can likewise declare synchronized blocks of statements using the following syntax:

synchronized(expr) {
   ...
}
where <tt>expr</tt> must evaluate to a reference type (i.e. for most purposes, an object reference). The code between the braces needn't have any connection to <tt>expr</tt>, although this would be perverse in most situations.

For an example, consider implementing a bounded buffer protocol. Bounded buffers are used in Unix to allow interprocess communication via <em>pipes</em>. When the output of one process is piped to the input of another, a bounded buffer is set up between the processes. The first process writes into the buffer, and the second process reads from it.

\includegraphics{gif/new_bounded_buffer.ps}

When the second process attempts to read data but there is none for it to read, it must wait until data is available. There are two general schemes that can be used to implement this waiting:

Naturally, Unix uses the second approach. Note that a symmetric situation occurs when the writer process attempts to write to the buffer and it is full: the writer is blocked pending the availability of space in the buffer. The writer may be reawakened when the reader reads some data and the routine for reading from the buffer determines that there is available space for more data to be written.

We can program a Java thread so that it busy waits on a locked object, but this is almost always a bad programming practice. Fortunately, Java provides a facility that enables us to avoid busy-waiting. There are two primitives,

which can be used inside synchronized methods and blocks. wait() puts the calling thread to sleep on a queue of threads that are waiting for some change in the status of a locked object. The thread can only be awakened if some other running thread calls notify() on the same locked object. When notify() is invoked for the locked object, a check is done to see whether any change has been made to it, and then some waiting thread from the associated queue is allowed to run. (Notify arbitrarily picks one thread).

The awakened process will, if prudently written, check whether the condition it is waiting for actually holds. If it does, the thread proceeds, and otherwise it should suspend itself again with another call to wait(). Usually, this is implemented in a loop. This may look a bit like busy-waiting, but because wait() suspends the calling thread until something of interest happens, most of the time the thread is idle.

There is also a notifyAll() method, which works just like notify() except that all waiting threads are allowed to run.


next up previous
Next: 3.2.1 Deadlock Up: 3.2 What is Concurrent Previous: 3.2 What is Concurrent
Corky Cartwright
2000-01-07