Atomic field updaters
The Java 5 atomic classes also include atomic field updaters.
These are essentially used as "wrappers" around a volatile field (primitive or
object reference). In truth, these wrappers are used inside the Java class libraries, but
probably aren't used much in user code. But it is worth taking a look at them to
see when they could be useful.
They are generally used when one or both of the following are true:
- You generally want to refer to the variable "normally" (that is, without
having to always refer to it via the get or set methods on the atomic classes)
but occasionally need an atomic get-set operation (which you can't do with
a normal volatile field);
- you are going to create a large number of objects of the given type, and
don't want every single instance to have to have an extra object embedded in it
just for atomic access.
An example of the first kind is BufferedInputStream. Every instance of
BufferedInputStream contains an internal buffer, defined as a volatile byte
array. Generally speaking, BufferedInputStream just needs to refer to this
array "normally" for the purpose of reading/writing bytes. However, an atomic get-and-set
operation is needed in specific places where the buffer array is replaced (either to
grow it or to mark it as null or closed), because closure can occur in a different
thread to that performing the reads. One option would have been to declare the buffer variable as an
AtomicReference to an array, and always get and set the array via this
variable. But that would be a bit messy. Instead, a noraml volatile array variable
is used, and the class also contains a static AtomicReferenceUpdater as
follows:
static AtomicReferenceFieldUpdater<BufferedInputStream, byte[]>
bufUpdater = AtomicReferenceFieldUpdater.newUpdater
(BufferedInputStream.class, byte[].class, "buf");
The array itself still remains a normal (volatile) variable, and reads and writes
from the array can use normal array syntax. But when necessary, via the field updater,
an atomic compare-and-set operation can be performed on the volatile variable.
Although the updater is static, each instance of BufferedInputStream has its
own buffer of course. So when we perform the CAS operation, we need to pass into the
updater the actual object whose variable we want to affect:
if (bufUpdater.compareAndSet(this, oldBuffer, newBuffer)) {
...
}
This is a call inside a method of BufferedInputStream, so this
refers to the particular instance of this class that the method is being called on,
and that object's variable will be affected.
Using atomic field updaters for linked nodes
Another use for atomic field updaters is to avoid creating a large number of
spurious atomic variables. For example, if we were creating a linked list structure
designed for concurrent access, we might want each node to have an atomic variable
pointing to the next node in the list. Judicious code can then allow different
parts of the list to be updated concurrently. Without atomic field updaters, this
would have meant code such as the following:
public class Node<T> {
private AtomicReference<Node<T>> next;
private T val;
...
}
In other words, every single node will have an additional AtomicReference
object embedded in it. Using an atomic field updater, we can get a performance gain
by declaring the 'next node' variable as a normal volatile object reference, and
then using a field updater (of which we declare a single, static, instance shared
by all nodes to access their respective 'next node' field):
public class Node<T> {
private volatile Node<T> next;
private T val;
...
}
As mentioned, atomic field updaters have been used in various places in the Java
class libraries. In reality, if you want an efficient currently accessible data structure,
it is strongly recommended to use one of the excellent standard implementations such
as ConcurrentHashMap, ConcurrentLinkedQueue or
(as of Java 6) ConcurrentSkipListMap.
Next...
On the next page, we move on to look at Java 5's concurrent collections,
with arguably the most important, the
ConcurrentHashMap class.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.