THE Java™ Programming Language, Fourth Edition

(Jeff_L) #1

perspective this set of values should only consist of a single value: that value most recently written by some
thread. However, as we shall see, in the absence of proper synchronization, the actual set of values available
may consist of many different values.


For example, suppose you had a field, currentValue, that was continuously displayed by a graphics
thread and that could be changed by other threads using a non-synchronized method:


public void updateCurrent() {
currentValue = (int) Math.random();
}


The display code might look something like this:


currentValue = 5;
for (;;) {
display.showValue(currentValue);
Thread.sleep(1000); // wait 1 second
}


When the loop is first entered (assuming no calls to updateCurrent have occurred in other threads) the
only possible value for currentValue is 5. However, because there is no synchronization, each time a
thread invokes updateCurrent the new value is added to the set of possible values to be read. By the time
currentValue is read in the loop, the possible values might consist of 5, 25, 42, and 326, any of which
may be returned by the read. According to the rules of the memory model any value written by some thread
(but not some "out of thin air" value) may be returned by the read. In practical terms, if there is no way for
showValue to change the value of currentValue, the compiler can assume that it can treat
currentValue as unchanged inside the loop and simply use the constant 5 each time it invokes
showValue. This is consistent with the memory model because 5 is one of the available values and the
memory model doesn't control which value gets returned. For the program to work as desired we have to do
something such that when currentValue is written, that written value becomes the only value that the
memory model will allow to be read. To do that we have to synchronize the writes and the reads of variables.


14.10.1. Synchronization Actions


Certain actions a thread performs are defined as synchronization actions, and the order in which these actions
are executed is termed the synchronization order of the programthe synchronization order is always consistent
with the program order. These synchronization actions synchronize reads and writes of variables.


The most familiar synchronization action is the use of synchronized methods and blocks to ensure exclusive
access to shared variables. The release of a monitor lock is said to synchronize with all subsequent
acquisitions and releases of that monitor lock. If all reads and writes to a variable occur only when a specific
monitor is held, then each read of the variable is guaranteed by the memory model to return the value that was
most recently written to it.


There is a second synchronization mechanism that doesn't provide the exclusive access of monitors, but that
again ensures that each read of a variable returns the most recently written valuethe use of volatile variables.
Fields (but not array elements) can be declared with the volatile modifier. A write to a volatile variable
synchronizes with all subsequent reads of that variable. If currentValue was declared as volatile then the
example code we showed would be correctly synchronized and the latest value would always be displayed.
The use of volatile variables is seldom a replacement for the use of synchronized methods or statements on its
own, because they don't provide atomicity across different actions. Rather, volatile variables are most often

Free download pdf