AtomicReference in Java - necessary for setting a reference in a thread-safe environment?
Asked Answered
N

5

5

In Java there exists an AtomicReference class. Does this mean that setting a reference is NOT an atomic operation in and of itself?

e.g., is this not thread-safe (assuming that the value returned cannot be modified)?:

public void someMethod()
{
   this.someList = Collections.unmodifiableList(new LinkedList<Object>());
}

public List<Object> getReadOnlyList()
{
   return someList;
}

How about in C#?

Niple answered 5/3, 2010 at 3:5 Comment(3)
Your example is not entirely thread-safe. If the list is unsafely published there you might not read the contents correctly (although the implementation of LinkedList may allow you to get away with this if it is empty).Filibeg
@Tom : do you mean other threads may get an old, cached value for the someList field, or that the someList field may actually be "half-written" ?Alveta
@hatchetman82 I mean that after the new reference is read, the object it points to may not have updated completely. So if I were to unsafely public new java.awt.Point(1, 2), I may be able to read the object in another thread but the x and y fields may still be zero.Filibeg
T
3

According to the Java Language Specification, version 3.0, Section 17.7:

Writes to and reads of references are always atomic, regardless of whether they are implemented as 32 or 64 bit values.

AtomicReference enables performing a compare and set as an atomic action.

This isn't threadsafe:

public boolean changeList(List<Object> oldValue, List<Object> newValue) { 
    if (this.someList == oldValue) {
        // someList could be changed by another thread after that compare,
        // and before this set
        this.someList = newValue;
        return true;
    }
    return false;
}
Turkestan answered 5/3, 2010 at 3:8 Comment(2)
Could do with some generics in the List parameter.Filibeg
use public <T> boolean changeList(List<T> oldValue, List<T> newValue) insteadJanicejanicki
G
3

The sometimes overlooked package description for java.util.concurrent.atomic elaborates on some common uses.

Addendum: Similarly, the package description for java.util.concurrent conveniently summarizes several essential points detailed in JLS §17.

Also, consider the potential benefit of Final Field Semantics if your List is meant to be immutable and a reference to it can be made final.

Genuflect answered 5/3, 2010 at 3:21 Comment(0)
E
3

Does this mean that setting a reference is NOT an atomic operation in and of itself?

Setting a reference variable is atomic, but an atomic operation is not necessarily thread-safe. Let me explain.

Atomic means that any observer (thread) sees the either the old value or the new value of the variable, and not something else. It does not mean that all observers see the new value when they look at the variable. (And as @Tom points out, atomicity of the reference variable says nothing about the atomicity properties of the object that it references.)

For all observers to see the new value in the variable, there needs to be some synchronization going on. For an update to a variable, this will happen if:

  • the variable is declared as volatile, or
  • access / updates to the variable are synchronized by the same primitive monitor lock.

A variable that is wrapped in the relevant "AtomicXxx" class will also be thread-safe, though you normally use one of these classes if you want to avoid locks and you want to do things such as atomic "compare-and-replace".

Again though, this only applies to the thread-safety of the object's reference. If the object's state is not also properly synchronized, a thread could well see stale values for the object's attributes, etcetera.

Euxenite answered 5/3, 2010 at 4:20 Comment(0)
M
2

If you don't use AtomicReference or the volatile keyword and the thread reading the reference is not the one that wrote to it, there is no guarantee that the reading thread will see the updated value.

This is especially true in a multi processor environment. The volatile keyword, and AtomicReference (which uses volatile internally for the basic set/get operations) enforce a memory barrier and cache flush making sure that the updated value is visible in main memory.

Multifarious answered 5/3, 2010 at 3:9 Comment(0)
N
1

As far as C#, I found the answer to this myself. Setting a reference is an Atomic operation as per section 5.5 of the C# language specification.

"Reads and writes of the following data types are atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list are also atomic. Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, are not guaranteed to be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement."

Niple answered 5/3, 2010 at 3:24 Comment(2)
The question is about thread safety, not atomicity. For example, reading a non-synchronized reference variable in Java can be atomic but not thread-safe.Euxenite
The point being that atomicity guarantees you will read either the old value of the reference (or null) or the new value of the reference but never part of one and part of the other.Ocko

© 2022 - 2024 — McMap. All rights reserved.