AtomicStampedReference 源码分析

摘要:

  1. CAS ABA 是什么
  2. AtomicStampedReference 是怎么解决 CAS ABA问题的

TOP 带着问题看源码

  1. CAS ABA 是什么
  2. AtomicStampedReference 是怎么解决 CAS ABA问题的

1. 基本介绍

AtomicStampedReference 是对 AtomicReference 的一个补充,解决了在 CAS 场景下 ABA 的问题

2. CAS ABA 是什么

从前面几篇分析,我们已经知道了 CAS 其实是一条 CPU 指令,作用是比较和替换,但是有可能 内存值原本是 A 然后变成 B 最后又变回了 A,这个时候 CAS 比较 A 发现是通过的(认为没有变化或者说是竞争),也就直接更新了,但是实际是有变化的。

一个解决思路就是加一个版本戳,每次更新变量同步更新一下版本号。这样就发现 1A != 3A,也就不会更新成功了。

回到 TOP 1 可以明白在并发情况下出现 ABA 的原因

3. 内部结构

1
2
3
4
5
6
7
8
9
10
11
12
13
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 标识。

4. AtomicStampedReference 的 CAS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 可以明白多加一个维度来保存版本更新信息即可解决。