摘要:
AtomicStampedReference 是对 AtomicReference 的一个补充,解决了在 CAS 场景下 ABA 的问题
从前面几篇分析,我们已经知道了 CAS 其实是一条 CPU 指令,作用是比较和替换,但是有可能 内存值原本是 A 然后变成 B 最后又变回了 A,这个时候 CAS 比较 A 发现是通过的(认为没有变化或者说是竞争),也就直接更新了,但是实际是有变化的。
一个解决思路就是加一个版本戳,每次更新变量同步更新一下版本号。这样就发现 1A != 3A,也就不会更新成功了。
回到 TOP 1 可以明白在并发情况下出现 ABA 的原因
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;
我们可以发现,AtomicStampedReference 对比 AtomicReference,全局维护的不是 T reference,而是 Pair。Pair 对象里多维护了一个 stamp 标识。
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return
// 1 引用没变
expectedReference == current.reference &&
// 2 版本号没变
expectedStamp == current.stamp &&
// 3 新引用等于旧引用
((newReference == current.reference &&
// 4 新版本号等于旧版本号
newStamp == current.stamp) ||
// 5 构造 Pair 然后 cas
casPair(current, Pair.of(newReference, newStamp)));
}
可以看到,最后的 return 的逻辑很复杂,我们可以看到多了版本号的校验。
回到 TOP 2 可以明白多加一个维度来保存版本更新信息即可解决。