Synchronization under the hood, and why Java 5 improves it (ctd)
(Continued from our discussion of
Java synchronization under the hood.)
Exposure of atomic instructions in Java 5
Having worked through this example, we can see that the synchronized
block involves an awful lot of baggage just to increment an integer variable. And
paradoxically, the underlying implementation actually uses a machine code instruction
designed to atomically update an integer variable! Rather than using CAS instructions
around a whole load of other lock housekeeping tasks, wouldn't it be great if we could just use a CAS
instruction to update the count variable? That way we do away with having to
waste memory and time on extra housekeeping variables
and with having to ensure memory access conditions on variables that are never affected
by trhe critical block of code. In the worst case, our code just loops a couple of times rather than
being context-switched out for several milliseconds to wait for a simple variable increment.
Java 5 effectively allows this.
The big synchronization breakthrough in Java 5 is that it effectively exposes
atomic instructions such as CAS to the Java programmer. The
java.util.concurrent package contains the AtomicInteger class
(and similar classes for other data types) allowing us to atomically
compare-and-set an integer (and variants such as increment-and-get, set-and-get etc).
Classes such as AtomicInteger are
essentially wrappers around atomic machine code instructions such as CAS.
For a simple counter class such as the one above, we can pretty much use
AtomicInteger as a drop-in replacement. But what's even more interesting is that
Java 5 includes a whole host of other synchronization and concurrency classes
already built around this new atomic functionality.
On the next page, we start our of Java 5 concurrency features by looking at
the atomic classes in Java 5.
Tightening up of volatile and final
The introduction of non-blocking atomic variable access is a key part of the
Java 5 concurrency improvements. Another improvement which is easy to miss is that
the definitions of volatile and final have been tightened up
slightly in order to allow them to be used with a couple of common programming
idioms: lazy initialisation and immutable objects. As of Java 5:
- Access to volatile variables have the same memory synchronization
and ordering behaviour as synchronized blocks.
- If an instance variable is declared final and its value set
in the constructor, that set value is guaranteed to be seen by another thread
as soon as it can see the object that holds that variable. This means that
we don't need synchronization to access an object for which all fields are
We'll look at what this means in more detail below, but first let's
get back to the atomic classes.