1

From the source code of AtomicLong:

    public final boolean compareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }

From the source code of AtomicLongFieldUpdater:

    public static <U> AtomicLongFieldUpdater<U> newUpdater(Class<U> tclass,
                                                           String fieldName) {
        Class<?> caller = Reflection.getCallerClass();
        if (AtomicLong.VM_SUPPORTS_LONG_CAS)
            return new CASUpdater<U>(tclass, fieldName, caller);
        else
            return new LockedUpdater<U>(tclass, fieldName, caller);
    }

    // CASUpdater extends AtomicLongFieldUpdater
        public final boolean compareAndSet(T obj, long expect, long update) {
            accessCheck(obj);
            return U.compareAndSwapLong(obj, offset, expect, update);
        }

    // LockedUpdater extends AtomicLongFieldUpdater
        public final boolean compareAndSet(T obj, long expect, long update) {
            accessCheck(obj);
            synchronized (this) {
                long v = U.getLong(obj, offset);
                if (v != expect)
                    return false;
                U.putLong(obj, offset, update);
                return true;
            }
        }

My question is why the two classes use different ways to update a long value? I. e. why does AtomicLongFieldUpdater conditionally fallback to the locking approach, while AtomicLong doesn't?

Petr Bodnár
  • 496
  • 3
  • 14
wk_wk
  • 11
  • 4
  • Relates to https://stackoverflow.com/questions/46817970/compareandset-on-processors-that-does-not-support-cas-operation and https://stackoverflow.com/questions/17239568/real-life-use-and-explanation-of-the-atomiclongfieldupdate-class. But none of the pages seem to question "why the difference"... – Petr Bodnár Dec 07 '19 at 11:46

1 Answers1

0

Both classes basically serve the same purpose, they both use the internal Unsafe class which does the "hard low-level" work. So it's only logical to question the implementation discrepancy.

I guess that the fallback done in AtomicLongFieldUpdater is maybe just a relic. This could be supported by the fact that VM_SUPPORTS_LONG_CAS is defined in the AtomicLong, but used only in the AtomicLongFieldUpdater.

Another possibility is that Java authors decided to somewhat optimize performance, but they did it just in the AtomicLongFieldUpdater.

The answer may be hidden in the javadoc of the VM_SUPPORTS_LONG_CAS:

    /**
     * Records whether the underlying JVM supports lockless
     * compareAndSwap for longs. While the Unsafe.compareAndSwapLong
     * method works in either case, some constructions should be
     * handled at Java level to avoid locking user-visible locks.
     */
    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();

I'm afraid we would have to ask authors directly to know the exact reasoning and meaning. It seems like nearly the same code was there already back in Java 5 - see the source code from the OpenJDK's "initial load" commit from 2007. I think it's hard to get to commits before this one...

Petr Bodnár
  • 496
  • 3
  • 14
  • Fun fact: these parts are present in the "src.zip" accompanying the older, closed source JDKs too. A blessed soul has 1.5.0 on GitHub: https://github.com/fanhongtao/JDK/tree/jdk_1.5.0/src/java/util/concurrent/atomic (but it does not contain anything else what OP and you have seen already). – tevemadar Dec 09 '19 at 15:21