Search code examples
javamultithreadingreflectionreferenceatomic

AtomicReference vs AtomicReferenceFieldUpdater, what's a purpose of AtomicReferenceFieldUpdater?


I'd like to atomically upgrade my reference. For example to use compareAndSet, getAndSet and other atomic operations.

I came from C++, so in C++ I've got volatile keyword and different atomic intrinsics, or the <atomic> API. In java, there's also a volatile keyword and different unsafe atomic operations.

By the way, there's also a well-documented AtomicReference (as well as Long, Integer, Boolean), so JDK creators provided us a way to safely execute atomic operations against references and primitives. There's nothing wrong about the API, it is rich and seems very familiar.

But, there's also an AtomicReferenceFieldUpdater whick provides a kinda weird way to execute atomic operations: you have to "find" the field via reflection by name and then you can use exactly the same operations.

So my questions are:

  1. What's the purpose of AtomicReferenceFieldUpdater at all? In my opinion it is implicit and kinda weird: you need to declare a volatile variable filed AND and the field updater itself. So where should I use a FieldUpdater?
  2. It provides an indirection: you're manipulating (not changing!) the FieldUpdater, not the variable, this confuses.
  3. Performance: as far as I know, both AtomicReferenceFieldUpdater and AtomicReference are delegating to the Unsafe, so their performance is similar, but anyway: are there any performance penalties of FieldUpdater agaist Reference?

Solution

  • It's used to save memory.

    Example from the doc:

    class Node {
       private volatile Node left, right;
    
       private static final AtomicReferenceFieldUpdater<Node, Node> leftUpdater =
         AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "left");
       private static AtomicReferenceFieldUpdater<Node, Node> rightUpdater =
         AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "right");
    
       Node getLeft() { return left; }
       boolean compareAndSetLeft(Node expect, Node update) {
         return leftUpdater.compareAndSet(this, expect, update);
       }
       // ... and so on
     }
    

    it declares left and right as Node directly. And the AtomicReferenceFieldUpdater is static final.

    Without AtomicReferenceFieldUpdater, you might need declare them as AtomicReference<Node>.

    private  AtomicReference<Node> left, right;
    

    which consumes more memory than Node. When there are many instances of Node, it consumes much more memory than first approach.