AtomicStampedReference

简介

AtomicStampedReference内部使用Pair来存储元素值及其版本号,主要用来解决ABA问题。

ABA问题

CAS操作可能存在ABA的问题,就是说:假如一个值原来是A,变成了B,又变成了A,那么CAS检查时会发现它的值没有发生变化,但是实际上却变化了。

如下代码存在ABA问题:

public static void main(String[] args) {
    AtomicInteger atomicInteger = new AtomicInteger(1);
    new Thread(()->{
        int value = atomicInteger.get();
        System.out.println("thread 1 read value: " + value);
        // 阻塞1s
        LockSupport.parkNanos(1000000000L);
        if (atomicInteger.compareAndSet(value, 3)) {
            System.out.println("thread 1 update from " + value + " to 3");
        } else {
            System.out.println("thread 1 update fail!");
        }
    }).start();

    new Thread(()->{
        int value = atomicInteger.get();
        System.out.println("thread 2 read value: " + value);
        if (atomicInteger.compareAndSet(value, 2)) {
            System.out.println("thread 2 update from " + value + " to 2");
            // do sth
            value = atomicInteger.get();
            System.out.println("thread 2 read value: " + value);
            if (atomicInteger.compareAndSet(value, 1)) {
                System.out.println("thread 2 update from " + value + " to 1");
            }
        }
    }).start();
}

AtomicStampedReference使用

AtomicStampedReference解决ABA问题:

public static void main(String[] args) {
    AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
    new Thread(()->{
        int[] stampHolder = new int[1];
        int value = atomicStampedReference.get(stampHolder);
        int stamp = stampHolder[0];
        System.out.println("thread 1 read value: " + value + ", stamp: " + stamp);

        // 阻塞1s
        LockSupport.parkNanos(1000000000L);

        if (atomicStampedReference.compareAndSet(value, 3, stamp, stamp + 1)) {
            System.out.println("thread 1 update from " + value + " to 3");
        } else {
            System.out.println("thread 1 update fail!");
        }
    }).start();

    new Thread(()->{
        int[] stampHolder = new int[1];
        int value = atomicStampedReference.get(stampHolder);
        int stamp = stampHolder[0];
        System.out.println("thread 2 read value: " + value + ", stamp: " + stamp);
        if (atomicStampedReference.compareAndSet(value, 2, stamp, stamp + 1)) {
            System.out.println("thread 2 update from " + value + " to 2");

            // do sth

            value = atomicStampedReference.get(stampHolder);
            stamp = stampHolder[0];
            System.out.println("thread 2 read value: " + value + ", stamp: " + stamp);
            if (atomicStampedReference.compareAndSet(value, 1, stamp, stamp + 1)) {
                System.out.println("thread 2 update from " + value + " to 1");
            }
        }
    }).start();
}

主要属性

// 内部类
private static class Pair<T> {
    // 存储元素
    final T reference;
    // 存储元素的版本号
    final int stamp;
    private Pair(T reference, int stamp) {
        this.reference = reference;
        this.stamp = stamp;
    }
    // 创建Pair的静态方法
    static <T> Pair<T> of(T reference, int stamp) {
        return new Pair<T>(reference, stamp);
    }
}

// 存储元素的Pair对象
private volatile Pair<V> pair;

// 构造函数,initialRef为初始值,initialStamp为初始的版本号
public AtomicStampedReference(V initialRef, int initialStamp) {
    pair = Pair.of(initialRef, initialStamp);
}

get()

public V get(int[] stampHolder) {
    Pair<V> pair = this.pair;
    stampHolder[0] = pair.stamp; // 将当前值的版本设置到stampHolder[0]
    return pair.reference; // 返回当前值
}

compareAndSet()

// expectedReference:期望的引用
// newReference:新的引用
// expectedStamp:期望版本号
// newStamp:新的版本号
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更新
}

private boolean casPair(Pair<V> cmp, Pair<V> val) {
    return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!