AtomicStampedReference内部使用Pair来存储元素值及其版本号,主要用来解决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解决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 {
// 存储元素
final T reference;
// 存储元素的版本号
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
// 创建Pair的静态方法
static Pair of(T reference, int stamp) {
return new Pair(reference, stamp);
}
}
// 存储元素的Pair对象
private volatile Pair pair;
// 构造函数,initialRef为初始值,initialStamp为初始的版本号
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
public V get(int[] stampHolder) {
Pair<V> pair = this.pair;
stampHolder[0] = pair.stamp; // 将当前值的版本设置到stampHolder[0]
return pair.reference; // 返回当前值
}
// 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);
}