Search code examples
javavolatileunsafecompare-and-swap

compareAndSwap a common member ( non-volatile member ) still has memory semantics of volatile read and write


When i read AbstractQueuedSynchronizer in jdk1.8, i see the comment that compareAndSetState method has memory semantics of a volatile read and write .

The comment and code are as follows:

/**
 * Atomically sets synchronization state to the given updated
 * value if the current state value equals the expected value.
 * This operation has memory semantics of a {@code volatile} read
 * and write.
 */
protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

In AbstractQueuedSynchronizer class, the stateOffset is a volatile member called state

Just wonder what memory semantics is if state is a non-volatile member ..


Solution

  • The semantics for compareAndSetState() are still that of a volatile read and write - this is the main reason why Unsafe.compareAndSwapInt() exists.

    But usually the code doesn't only call Unsafe.compareAndSwapInt(). Usually the code reads the current value, calculates a new value and then tries to replace the value it has read with the new value. And this read also must be done using volatile read semantics.

    For example, in CountDownLatch.Sync#tryReleaseShared():

       protected boolean tryReleaseShared(int releases) {
           // Decrement count; signal when transition to zero
           for (;;) {
               int c = getState();
               if (c == 0)
                   return false;
               int nextc = c-1;
               if (compareAndSetState(c, nextc))
                   return nextc == 0;
           }
       }
    

    Reading the state with getState() must use volatile read semantics to make sure it uses the latest value.

    And yes, in principle you could refuse to declare a field like state as volatile. But then you must make sure that every access to that field goes through compareAndSwapInt(), getIntVolatile() and putIntVolatile(). If some maintainer of the code forgets this rules and adds a single direct read of the field your code will break in unexpected ways at the most disadvantageous moment in time - for example while giving a presentation to an important customer.