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#?

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
Cornelius
  • 71
  • 1
  • 4
  • 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). – Tom Hawtin - tackline Mar 05 '10 at 03:20
  • @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" ? – radai Mar 05 '10 at 05:13
  • @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. – Tom Hawtin - tackline Mar 05 '10 at 16:00

5 Answers5

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;
}
Stephen Denne
  • 36,219
  • 10
  • 45
  • 60
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.

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
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.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
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.

Michael Barker
  • 14,153
  • 4
  • 48
  • 55
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."

Cornelius
  • 71
  • 1
  • 4
  • 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. – Stephen C Mar 05 '10 at 03:38
  • 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. – Lawrence Dol Mar 05 '10 at 04:23