Class - java.util.concurrent.atomic.AtomicStampedReference
Created by : Mr Dk.
2020 / 06 / 25 11:39
Nanjing, Jiangsu, China
Definition
这个类将一个 int 类型的 stamp 与一个对象引用一起维护,两者将被共同地原子地更新。Stamp 的作用相当于一个版本号,每当对象引用被修改时,同时更新这个 stamp。有了 stamp,就可以解决 ABA 问题。
/**
 * An {@code AtomicStampedReference} maintains an object reference
 * along with an integer "stamp", that can be updated atomically.
 *
 * <p>Implementation note: This implementation maintains stamped
 * references by creating internal objects representing "boxed"
 * [reference, integer] pairs.
 *
 * @since 1.5
 * @author Doug Lea
 * @param <V> The type of object referred to by this reference
 */
public class AtomicStampedReference<V> {
}
Internal State
内部维护的状态是一个 volatile 修饰的 pair,而 pair 中包含了对象引用与 stamp:
private static class Pair<T> {
    final T reference;
    final int stamp;
    private Pair(T reference, int stamp) {
        this.reference = reference;
        this.stamp = stamp;
    }
    static <T> Pair<T> of(T reference, int stamp) {
        return new Pair<T>(reference, stamp);
    }
}
private volatile Pair<V> pair;
Constructor
在构造对象时,传入对象引用和初始版本号:
/**
 * Creates a new {@code AtomicStampedReference} with the given
 * initial values.
 *
 * @param initialRef the initial reference
 * @param initialStamp the initial stamp
 */
public AtomicStampedReference(V initialRef, int initialStamp) {
    pair = Pair.of(initialRef, initialStamp);
}
Getter
获取内部状态。可以分别获取对象引用或 stamp,也可以直接一次性获取:
/**
 * Returns the current value of the reference.
 *
 * @return the current value of the reference
 */
public V getReference() {
    return pair.reference;
}
/**
 * Returns the current value of the stamp.
 *
 * @return the current value of the stamp
 */
public int getStamp() {
    return pair.stamp;
}
/**
 * Returns the current values of both the reference and the stamp.
 * Typical usage is {@code int[1] holder; ref = v.get(holder); }.
 *
 * @param stampHolder an array of size of at least one.  On return,
 * {@code stampholder[0]} will hold the value of the stamp.
 * @return the current value of the reference
 */
public V get(int[] stampHolder) {
    Pair<V> pair = this.pair;
    stampHolder[0] = pair.stamp;
    return pair.reference;
}
Setter
无条件设置内部状态:
/**
 * Unconditionally sets the value of both the reference and stamp.
 *
 * @param newReference the new value for the reference
 * @param newStamp the new value for the stamp
 */
public void set(V newReference, int newStamp) {
    Pair<V> current = pair;
    if (newReference != current.reference || newStamp != current.stamp)
        this.pair = Pair.of(newReference, newStamp);
}
CAS Implementation
首先通过 Unsafe 类获得 pair 数据的地址偏移,然后对该数据整体进行 CAS:
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
private static final long pairOffset =
    objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);
private boolean casPair(Pair<V> cmp, Pair<V> val) {
    return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
                                String field, Class<?> klazz) {
    try {
        return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
    } catch (NoSuchFieldException e) {
        // Convert Exception to corresponding Error
        NoSuchFieldError error = new NoSuchFieldError(field);
        error.initCause(e);
        throw error;
    }
}
CAS
当且仅当对象引用和 stamp 同时与期望值相等 (或没有发生改变) 时,CAS 才会成功:
/**
 * Atomically sets the value of both the reference and stamp
 * to the given update values if the
 * current reference is {@code ==} to the expected reference
 * and the current stamp is equal to the expected stamp.
 *
 * <p><a href="package-summary.html#weakCompareAndSet">May fail
 * spuriously and does not provide ordering guarantees</a>, so is
 * only rarely an appropriate alternative to {@code compareAndSet}.
 *
 * @param expectedReference the expected value of the reference
 * @param newReference the new value for the reference
 * @param expectedStamp the expected value of the stamp
 * @param newStamp the new value for the stamp
 * @return {@code true} if successful
 */
public boolean weakCompareAndSet(V   expectedReference,
                                    V   newReference,
                                    int expectedStamp,
                                    int newStamp) {
    return compareAndSet(expectedReference, newReference,
                            expectedStamp, newStamp);
}
/**
 * Atomically sets the value of both the reference and stamp
 * to the given update values if the
 * current reference is {@code ==} to the expected reference
 * and the current stamp is equal to the expected stamp.
 *
 * @param expectedReference the expected value of the reference
 * @param newReference the new value for the reference
 * @param expectedStamp the expected value of the stamp
 * @param newStamp the new value for the stamp
 * @return {@code true} if successful
 */
public boolean compareAndSet(V   expectedReference,
                                V   newReference,
                                int expectedStamp,
                                int newStamp) {
    Pair<V> current = pair;
    return
        expectedReference == current.reference &&
        expectedStamp == current.stamp &&
        ((newReference == current.reference &&
            newStamp == current.stamp) ||
            casPair(current, Pair.of(newReference, newStamp)));
}
以下函数试图通过 CAS 原子地设置 pair 中的版本号。当然前提是期望的对象引用需要与 pair 中的对象引用一致,不然说明其它线程修改了对象引用,再设置版本号是不安全的。对象引用一致后,试图将对象引用与新的版本号通过 CAS 更新到内部状态中:
/**
 * Atomically sets the value of the stamp to the given update value
 * if the current reference is {@code ==} to the expected
 * reference.  Any given invocation of this operation may fail
 * (return {@code false}) spuriously, but repeated invocation
 * when the current value holds the expected value and no other
 * thread is also attempting to set the value will eventually
 * succeed.
 *
 * @param expectedReference the expected value of the reference
 * @param newStamp the new value for the stamp
 * @return {@code true} if successful
 */
public boolean attemptStamp(V expectedReference, int newStamp) {
    Pair<V> current = pair;
    return
        expectedReference == current.reference &&
        (newStamp == current.stamp ||
            casPair(current, Pair.of(expectedReference, newStamp)));
}
顺带一提
java.util.concurrent.atomic.AtomicMarkableReference 的实现与 AtomicMarkableReference 几乎一样。区别在于,AtomicMarkableReference 的 pair 内部维护的不是版本号,而是一个 boolean 类型的变量,代表对象引用是否被修改过。显然,在实现上会更简单。
AtomicStampedReference 基本上可以替代 AtomicMarkableReference。因为后者只能记录对象引用是否被修改过,而前者还可以记录对象引用被修改了几次。
/**
 * An {@code AtomicMarkableReference} maintains an object reference
 * along with a mark bit, that can be updated atomically.
 *
 * <p>Implementation note: This implementation maintains markable
 * references by creating internal objects representing "boxed"
 * [reference, boolean] pairs.
 *
 * @since 1.5
 * @author Doug Lea
 * @param <V> The type of object referred to by this reference
 */
public class AtomicMarkableReference<V> {
    private static class Pair<T> {
        final T reference;
        final boolean mark;
        private Pair(T reference, boolean mark) {
            this.reference = reference;
            this.mark = mark;
        }
        static <T> Pair<T> of(T reference, boolean mark) {
            return new Pair<T>(reference, mark);
        }
    }
    private volatile Pair<V> pair;
    // ...
}