Java volatile reference vs. AtomicReference

JavaConcurrency

Java Problem Overview


Is there any difference between a volatile Object reference and AtomicReference in case I would just use get() and set()-methods from AtomicReference?

Java Solutions


Solution 1 - Java

Short answer is: No.

From the java.util.concurrent.atomic package documentation. To quote:

>The memory effects for accesses and updates of atomics generally follow the rules for volatiles:

>- get has the memory effects of reading a volatile variable.

  • set has the memory effects of writing (assigning) a volatile variable.

By the way, that documentation is very good and everything is explained.


AtomicReference::lazySet is a newer (Java 6+) operation introduced that has semantics unachievable through volatile variables. See [this post][3] for more information.

[3]: https://stackoverflow.com/a/1468020/591495)

Solution 2 - Java

No, there is not.

The additional power provided by AtomicReference is the compareAndSet() method and friends. If you do not need those methods, a volatile reference provides the same semantics as AtomicReference.set() and .get().

Solution 3 - Java

There are several differences and tradeoffs:

  1. Using an AtomicReference get/set has the same JMM semantics as a volatile field(as the javadoc states), but the AtomicReference is a wrapper around a reference, so any access to the field involves a further pointer chase.

  2. The memory footprint is multiplied (assuming a compressed OOPs environment, which is true for most VMs):

  • volatile ref = 4b
  • AtomicReference = 4b + 16b (12b object header + 4b ref field)
  1. AtomicReference offers a richer API than a volatile reference. You can regain the API for the volatile reference by using an AtomicFieldUpdater, or with Java 9 a VarHandle. You can also reach straight for sun.misc.Unsafe if you like running with scissors. AtomicReference itself is implemented using Unsafe.

So, when is it good to choose one over the other:

  • Only need get/set? Stick with a volatile field, simplest solution and lowest overhead.
  • Need the extra functionality? If this is a performance(speed/memory overhead) sensitive part of your code make a choice between AtomicReference/AtomicFieldUpdater/Unsafe where you tend to pay in readability and risk for your performance gain. If this not a sensitive area just go for AtomicReference. Library writers typically use a mix of these methods depending on targeted JDKs, expected API restrictions, memory constraints and so on.

Solution 4 - Java

JDK source code is one of the best ways to answers confusions like this. If you look at the code in AtomicReference, it uses a volatie variable for object storage.

private volatile V value;

So, obviously if you are going to just use get() and set() on AtomicReference it is like using a volatile variable. But as other readers commented, AtomicReference provides additional CAS semantics. So, first decide if you want CAS semantics or not, and if you do only then use AtomicReference.

Solution 5 - Java

AtomicReference provides additional functionality which a plain volatile variable does not provide. As you have read the API Javadoc you will know this, but it also provides a lock which can be useful for some operations.

However, unless you need this additional functionality I suggest you use a plain volatile field.

Solution 6 - Java

Sometimes even if you only use gets and sets, AtomicReference might be a good choice:

Example with volatile:

private volatile Status status;
...
public setNewStatus(Status newStatus){
  status = newStatus;
}

public void doSomethingConditionally() {
  if(status.isOk()){
    System.out.println("Status is ok: " + status); // here status might not be OK anymore because in the meantime some called setNewStatus(). setNewStatus should be synchronized
  }
}

The implementation with AtomicReference would give you a copy-on-write synchronization for free.

private AtomicReference<Status> statusWrapper;
...

public void doSomethingConditionally() {
  Status status = statusWrapper.get();
  if(status.isOk()){
    System.out.println("Status is ok: " + status); // here even if in the meantime some called setNewStatus() we're still referring to the old one
  }
}

One might say that you could still could have a proper copy if you substituted:

Status status = statusWrapper.get();

with:

Status statusCopy = status;

However the second one is more likely to be removed by someone accidentally in the future during "code cleaning".

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionauramoView Question on Stackoverflow
Solution 1 - JavapgrasView Answer on Stackoverflow
Solution 2 - JavaAviView Answer on Stackoverflow
Solution 3 - JavaNitsan WakartView Answer on Stackoverflow
Solution 4 - JavaendlessView Answer on Stackoverflow
Solution 5 - JavaPeter LawreyView Answer on Stackoverflow
Solution 6 - JavanmeView Answer on Stackoverflow