🤔思路分析
-
CAS 存在一个经典问题:
如果值从 A 变为 B 再变回 A,CAS 会认为没有变化,但实际上变量已被修改过。
解决方案 :使用带版本戳的原子类版本戳解决ABA问题的思路很直接:把一次简单的CAS,变成"值+版本号"的配对CAS。多了一个维度,就多了一层保障。
-
在 Java 中,
AtomicStampedReference通过一个内部的Pair对象将引用值和版本号(stamp)捆绑起来,并基于volatile可见性和 CAS 原子性,实现了整个"配对"的原子更新。
🧱 内部结构:配对的核心载体
AtomicStampedReference 的精髓在于其静态内部类 Pair,它充当了原子操作的单元。
java
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; // 核心字段,volatile保证可见性
Pair的不可变性 :reference和stamp都被final修饰。这意味着,每次更新状态时,都不是修改旧对象,而是创建一个全新的Pair实例来替换旧的。volatile的可见性 :pair变量被volatile修饰,这确保了一个线程对pair的更新能立即对其他线程可见。
⚙️ 核心操作:compareAndSet 源码解析
compareAndSet 是整个机制的枢纽。它校验当前引用 和当前版本戳是否都符合预期,只有两者都匹配,才会执行更新。
java
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair; // (1) 获取当前的 Pair 对象
// (2) 核心校验:引用和版本戳都必须匹配预期值
return expectedReference == current.reference &&
expectedStamp == current.stamp &&
// (3) 优化:如果新值和旧值完全相同,直接返回成功,避免无意义的CAS
((newReference == current.reference && newStamp == current.stamp) ||
// (4) 核心CAS操作:尝试用新Pair对象替换旧Pair对象
casPair(current, Pair.of(newReference, newStamp)));
}
// 底层CAS实现,依赖Unsafe类
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
- 逻辑拆解
- 获取当前快照 :
Pair<V> current = pair;获取当前最新的Pair对象。 - 双重校验 :必须同时满足
expectedReference == current.reference(引用匹配)和expectedStamp == current.stamp(版本戳匹配),CAS才能继续。 - 无变化优化 :如果发现
newReference和newStamp与当前值一模一样,则直接返回true,这是一种避免无意义CAS的自优化手段。 - CAS替换 :通过
casPair方法执行底层的compareAndSwapObject,尝试将pair引用指向新创建的Pair.of(newReference, newStamp)对象。这一步是原子的,是最终保障。
- 获取当前快照 :
✅ 实战对比:AtomicStampedReference 如何精准拦截 ABA
下面通过一个代码示例,直观地对比 AtomicReference(无版本戳)和 AtomicStampedReference(有版本戳)在处理ABA问题时的不同表现。
java
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
public class ABADemo {
public static void main(String[] args) throws InterruptedException {
// 模拟没有版本戳的ABA问题
AtomicReference<Integer> ref = new AtomicReference<>(1);
Thread t1 = new Thread(() -> {
Integer value = ref.get(); // value = 1
try { Thread.sleep(100); } catch (InterruptedException e) {}
// 尽管中间被改成2又改回1,但由于值还是1,CAS成功!
System.out.println("AtomicReference CAS result: " + ref.compareAndSet(value, 3));
});
Thread t2 = new Thread(() -> {
ref.set(2);
ref.set(1);
});
t1.start(); t2.start();
t1.join(); t2.join(); // 输出: AtomicReference CAS result: true
// 模拟使用AtomicStampedReference解决ABA问题
AtomicStampedReference<Integer> stampedRef = new AtomicStampedReference<>(1, 0);
Thread t3 = new Thread(() -> {
int[] stampHolder = new int[1];
Integer value = stampedRef.get(stampHolder); // 获取当前值和版本戳
int stamp = stampHolder[0];
try { Thread.sleep(100); } catch (InterruptedException e) {}
// 尽管值还是1,但版本戳0已不匹配(现在是2),所以CAS失败!
System.out.println("AtomicStampedReference CAS result: " +
stampedRef.compareAndSet(value, 3, stamp, stamp + 1));
});
Thread t4 = new Thread(() -> {
// 执行ABA操作,每次修改都递增版本戳
int[] stampHolder = new int[1];
int value = stampedRef.get(stampHolder);
int stamp = stampHolder[0];
stampedRef.compareAndSet(value, 2, stamp, stamp + 1);
value = stampedRef.get(stampHolder);
stamp = stampHolder[0];
stampedRef.compareAndSet(value, 1, stamp, stamp + 1);
});
t3.start(); t4.start();
t3.join(); t4.join(); // 输出: AtomicStampedReference CAS result: false
}
}
🆚 补充:与 AtomicMarkableReference 的区别
JDK 还提供了 AtomicMarkableReference,它也使用 Pair,但 stamp 被替换成了 boolean mark,只记录对象是否被修改过,不关心修改次数。
| 特性 | AtomicStampedReference |
AtomicMarkableReference |
|---|---|---|
| 版本戳类型 | int,可记录修改次数 |
boolean,只记录是否被修改 |
| 适用场景 | 需要精确知晓变量被修改了多少次 | 只关心变量是否曾被修改过 |
| 示例 | 账户余额变动次数 | 订单是否已被处理 |
⚠️ 注意事项
- 动态获取版本戳 :强烈推荐使用
get(int[] stampHolder)方法来一次性获取当前的引用值和版本戳,这避免了分别调用getReference()和getStamp()带来的时间差问题。 - 版本戳选择 :通常使用一个单调递增的整数(例如
stamp+1)作为新版本戳即可满足大多数业务需求,这能充分保证每个版本的唯一性。
可以看到,AtomicStampedReference 通过引入版本戳,在硬件层面实现了一套可靠的"版本控制"方案,为解决ABA问题提供了精准的武器。