AtomicReference in Java - necessary for setting a reference in a thread-safe environment?
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 someL开发者_StackOverflow社区ist;
}
How about in C#?
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;
}
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
.
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.
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.
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."
精彩评论