6

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).

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386

1 Answers1

2

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

Community
  • 1
  • 1
tzaman
  • 46,925
  • 11
  • 90
  • 115
  • Right, but the question isn't really about re-ordering: indeed the `lazySet` and `compareAndSwap` don't occur on the same thread, so there in no inherent order between them. They will occur in _some_ order, and the question is more or less whether the atomic nature of CAS means that will never clobber the write. – BeeOnRope Jan 28 '15 at 00:27
  • Ordering here refers to the memory operations, not the instructions; which thread they're on isn't a factor. CAS is a load and a store - since lazySet applies a _preceeding store-store barrier_, if the CAS already happened then the write is guaranteed to complete before lazySet updates it. – tzaman Jan 28 '15 at 20:06
  • Yes memops are the only relevant instructions. Which thread they are on is important - on the same thread, there would be no issue. On the other hand, the two operations on different threads have no inherent order. Basically a statement like: "this means the lazySet write will not be allowed to be reordered around it" makes sense for two operations on the same thread, but on different threads they could occur in either order, other than restrictions imposed by any actual "synchronizes with" operations at runtime (e.g., a volatile read that sees a volatile write from another thread). – BeeOnRope Jan 29 '15 at 00:51
  • If the CAS write hits first, the store-store barrier means it has to complete before the lazySet write goes in. i.e. the lazySet write cannot be reordered to happen before the CAS write (which is the only way it could get clobbered.) – tzaman Jan 29 '15 at 01:05
  • Makes sense. I guess an open question is imagine the lazy write was now simply a normal plain non-volatile write. The question is then can it be clobbered, now that it has no special barriers at all? I still think not because even if it goes before the CAS, the CAS should fail since the read and write are _atomic_. That's what's not clear to me. – BeeOnRope Jan 29 '15 at 05:22