AtomicStampedReference源码分析
问题
- AtomicStampedReference是什么?
- AtomicStampedReference怎么解决ABA问题?
参考
彤哥读源码
一:简介
AtomicStampedReference是Java并发包下面提供的一个原子类,它能解决其他原子类无法解决的ABA问题。
ABA问题在前面的Unsafe类学习中有提到。它发生在多线程环境中,当某线程连续读取同一块内存地址两次,两次得到的值一样,它简单地认为“此内存地址的值并没有被修改过”,然而,同时可能存在另一个线程在着这两次读取之间把这个内存地址的值从A修改成了B又修改回了A,这时还简单地认为“没有修改过”显然是错误的。
彤哥在他的文章中举了两个很好的例子来表明ABA的问题,很形象。
二:源码分析
属性
// 内部定义类Pair<T>,两个属性,reference,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);
}
}
// 声明一个Pair类型变量,在上面已经定义
private volatile Pair<V> pair;
// 获取Unsafe实例
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
// 获取pair变量的偏移量,方便寻址
private static final long pairOffset =
objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);
方法
构造方法
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
构造方法只有一个,需要传入两个参数,一个是初始值,一个是初始版本号。
普通get/set方法
// 返回值
public V getReference() {
return pair.reference;
}
// 返回版本号
public int getStamp() {
return pair.stamp;
}
// 返回值,参数接收版本号
public V get(int[] stampHolder) {
Pair<V> pair = this.pair;
stampHolder[0] = pair.stamp;
return pair.reference;
}
public void set(V newReference, int newStamp) {
Pair<V> current = pair;
if (newReference != current.reference || newStamp != current.stamp)
this.pair = Pair.of(newReference, newStamp);
}
compareAndSet()方法
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
// 获取当前pair
Pair<V> current = pair;
return
// 判断当前reference有没有变化
expectedReference == current.reference &&
// 判断版本号有没有变化
expectedStamp == current.stamp &&
// 判断要更改的reference和版本号是否与当前的pair一致,是则不用更改,否则更新
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
调用了Unsafe类中的CAS方法,实现了原子操作,并通过版本号的引入克服了ABA问题。
三:总结
整个AtomicStampedReference类的源码很简洁,构建了内部类Pair来存储元素值和版本号,通过版本号的引入来解决ABA问题。