CAS引发的ABA问题
AtomicStampReference可以解决ABA问题
public class AtomicStampReferenceTest {
private static AtomicStampedReference atomicRef = new AtomicStampedReference<Integer>(100,0);
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
boolean b = atomicRef.compareAndSet(100, 101, atomicRef.getStamp(), atomicRef.getStamp() + 1);
System.out.println(b + "==="+atomicRef.getReference()+"==="+atomicRef.getStamp());
boolean b1 = atomicRef.compareAndSet(101, 100, atomicRef.getStamp(), atomicRef.getStamp() + 1);
System.out.println(b1 + "==="+atomicRef.getReference()+"==="+atomicRef.getStamp());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
int stamp = atomicRef.getStamp();
System.out.println("Before sleep:"+stamp);
Thread.sleep(2);
boolean b = atomicRef.compareAndSet(100, 101, stamp, stamp + 1);
System.out.println(b+"");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
}
}
当前值为100,戳为0
第一次期望值为100,期望戳为0,待更新值为101,戳为1,更新成功
此时当前值为101,戳为1
第二次期望值为101,期望戳为1,待更新值为100,戳为2,更新成功
此时当前值为100,戳为2
第三次期望值100,期望戳为0,待更新值为101,戳为1,更新失败
AtomicStampReference源码解析
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;
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
// 当前的值和版本都和期望值一样,且待更新的值和版本和当前的值和版本一样,返回true
// 当前的值和版本都和期望值一样,且如果当前的pair和内存中的一样,则更新成功
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
// 获取pair属性在内存中的偏移量
private static final long pairOffset =
objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);
private boolean casPair(Pair<V> cmp, Pair<V> val) {
// cmp 代表当前的pair val代表待更新的pair
// 如果当前的pair和内存中的一样,则更新成功
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;
}
}