The memory model section (17.4) of the JLS describes in reasonable detail the semantics of volatile
and non-volatile
reads and writes, as well as interaction with certain other constructs such as monitor enter and exit.
However, it doesn't completely explain the semantics of the compareAndSwap
nor lazySet
on the java.util.concurrent.Atomic* classes. For compareAndSet
, you do have the blurb from the package javadoc:
compareAndSet and all other read-and-update operations such as getAndIncrement
have the memory effects of both reading and writing volatile variables.
lazySet
offers the somewhat more inscrutable blurb:
lazySet has the memory effects of writing (assigning) a volatile variable
except that it permits reorderings with subsequent (but not previous)
memory actions that do not themselves impose reordering constraints with
ordinary non-volatile writes. Among other usage contexts, lazySet may apply
when nulling out, for the sake of garbage collection, a reference that is
never accessed again.
What isn't clear to me is how they interact. If you issue a CAS (compareAndSet
) and a lazySet to the same atomic value, where the CAS expectedValue is different than the lazySet value, is it possible that the CAS overwrites the lazySet value?
More explicitly, given two threads, T1 and T2, operating on a common AtomicInteger atomic = new AtomicInteger();
as follows:
static CountDownLatch latch = new CountDownLatch(2);
T1
atomic.lazySet(5); // L1A
latch.countDown();
latch.await();
int val1 = atomic.get();
T2
atomic.compareAndSet(0, 10); // L2A
latch.countDown();
latch.await();
int val2 = atomic.get();
Is val1 == val2 == 10
a possible scenario here? Indeed, can val1
or val2
ever be 10?
The latches aren't core to the question - they are just a way of having both threads wait until the other is done, and forcing a happens before between the the interesting lazySet
and compareAndSet
operations on each thread, and the later reads of the atomic to see the state (without them, you could certainly see at least val2 == 10
, transiently).
compareAndSet
is both a read and a write, therefore it does impose a write-ordering constraint. Per the docs, this means the lazySet
write will not be allowed to be reordered around it. So no, val1
and val2
should never be 10.
EDIT: To clarify, what lazySet
essentially does is that it acts an atomic write for the purposes of any other atomic operation that also writes to the same thing, but non-atomic for other atomic operations that are only reading.
More potentially useful discussion at AtomicInteger lazySet vs. set, the most useful tidbit being the link to the original changeset where the lazy methods were added: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6275329
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With