From the Java AtomicReferenceFieldUpdater
docs:
Note that the guarantees of the
compareAndSet
method in this class are weaker than in other atomic classes. Because this class cannot ensure that all uses of the field are appropriate for purposes of atomic access, it can guarantee atomicity and volatile semantics only with respect to other invocations ofcompareAndSet
andset
.
This means I can't do normal volatile writes along with compareAndSet
, but have to use set
instead. It doesn't mention anything about get
.
Does that mean that I can still read volatile fields with the same atomicity guarantees - all writes before the set
or compareAndSet
are visible to everybody who has read the volatile field being?
Or do I have to use get
on the AtomicReferenceFieldUpdater
instead of volatile reads on the field?
Please post references if you have them.
Thank you.
EDIT:
From Java Concurrency in Practice, the only thing they say:
The atomicity guarantees for the updater classes are weaker than for the regular atomic classes because you cannot guarantee that the underlying fields will not be modified directly — the compareAndSet and arithmetic methods guarantee atomicity only with respect to other threads using the atomic field updater methods.
Again, no mention of how the other threads are supposed to read these volatile fields.
Also, am I right to assume that "modified directly" is a regular volatile write?
As explained in the package documentation for atomics (in general, not the updaters specifically):
What problem is an atomic's
compareAndSet
trying to solve? Why use (for example)atomicInteger.compareAndSet(1,2)
instead ofif(volatileInt == 1) { volatileInt = 2; }
? It's not trying to solve any problem with concurrent reads, because those are already taken care of by a regularvolatile
. (A "volatile" read or write is the same as an "atomic" read or write. A concurrent read would only be a problem if it happened in the middle of a write, or if statements were reordered or optimized in some problematic way; butvolatile
already prevents those things.) The only problem thatcompareAndSet
solves is that, in thevolatileInt
approach, some other thread might come in with a concurrent write, between when we readvolatileInt
(volatileInt == 1
) and when we write to it (volatileInt = 2
).compareAndSet
solves this problem by locking out any competing writes during that time.This is equally true in the specific case of the "updaters" (
AtomicReferenceFieldUpdater
etc.):volatile
reads are still just peachy. The updaters'compareAndSet
methods' only limitation is that, instead of "locking out any competing writes" as I wrote above, they only lock out competing writes from the same instance ofAtomicReferenceFieldUpdater
; they can't protect you when you're concurrently updating avolatile
field directly (or, for that matter, when you're concurrently using multipleAtomicReferenceFieldUpdater
s to update the samevolatile
field). (Incidentally, depending how you look at it — the same is true ofAtomicReference
and its kin: if you were to update their fields in a way that bypassed their own setters, they couldn't protect you. The difference is that anAtomicReference
actually owns its field, and it'sprivate
, so there's no need to warn you against somehow modifying it by external means.)So, to answer your question: Yes, you can continue to read
volatile
fields with the same atomicity guarantees against partial/inconsistent reads, against statements being reordered, etc.Edited to add (Dec 6): Anyone who's particularly interested in this subject will probably be interested in the discussion immediately below. I was asked to update the answer to clarify salient points from that discussion:
I think the most important point to add is that the above is my own interpretation of the documentation. I'm fairly confident that I have understood it correctly, and that no other interpretation makes sense; and I can, if desired, argue the point at length ;-) ; but neither I nor anyone else has produced any references to any authoritative document that addresses this point any more explicitly than the two documents mentioned in the question itself (the class's Javadoc and Java Concurrency in Practice) and the one document mentioned in my original answer to it above (the package's Javadoc).
The next most important point, I think, is that although the documentation for
AtomicReferenceUpdater
says that it's unsafe to mixcompareAndSet
with a volatile write, I believe that on typical platforms it actually is safe. It's unsafe only in the general case. I say this because of the following comment from the package documentation:So:
AtomicReference.set
simply uses a volatile write, sinceAtomicReference.compareAndSet
uses a compare-and-swap operation that is atomic with respect to volatile writes.AtomicReferenceUpdater.set
is necessarily more complex thanAtomicReference.set
, because it has to use reflection-like logic to update a field in another object, but I maintain that that is the only reason it is more complex. A typical implementation callsUnsafe.putObjectVolatile
, which is a volatile write by longer name.compareAndSet
could be implemented by (more or less) applyingsynchronized
to a method that usesget
andset
straightforwardly. But for this to work,set
must also besynchronized
, for the reason explained in my original answer above; that is, it can't just be a volatile write, because then it could modify the field aftercompareAndSet
has calledget
but beforecompareAndSet
callsset
.In Sun's JDK 1.6.0_05 implementation of
java.util.concurrent.ConcurrentLinkedQueue<E>
, we find this:(note: whitespace adjusted for compactness), where, once an instance has been constructed, there are no volatile writes — that is, all writes are via
AtomicReferenceFieldUpdater.compareAndSet
orAtomicReferenceFieldUpdater.set
— but volatile reads appear to be used freely, without a single call toAtomicReferenceFieldUpdater.get
. Later releases of JDK 1.6 were changed to useUnsafe
directly (this had happened by Oracle's JDK 1.6.0_27), but discussions on the JSR 166 mailing list attribute this change to performance considerations rather than to any qualm about the correctness of the previous implementation.AtomicReferenceFieldUpdater.set
; but someone who doesn't accept my interpretation of the one point may not accept my interpretation of the other, and might argue that the above code is not meant to be safe for all platforms.Node
seems to allow volatile reads to take place concurrently with calls toAtomicReferenceFieldUpdater.compareAndSet
, it's a private class; and I have not undertaken any proof that its owner (ConcurrentLinkedQueue
) actually makes such calls without its own precautions. (But although I have not proven the claim, I doubt that anyone would dispute it.)Please see the below comments for background on this addendum, and for further discussion.