在高并发编程中,CAS(Compare And Swap)操作是很多无锁并发工具的核心实现,但CAS操作存在一个典型的ABA问题:如果一个变量的值从A被改成B,再被改回A,CAS操作无法感知到这个中间变化过程,就会误认为变量没有被修改过,进而导致状态判断失真。AtomicStampedReference通过引入戳记变量的设计,完美解决了这个问题。

AtomicStampedReference的核心设计
AtomicStampedReference内部维护了一个Pair对象,这个对象同时存储了对象的引用和一个整型的戳记(stamp)。每次对引用进行修改时,都需要同时更新引用值和戳记值,戳记会随着每次修改自动递增,这样即使引用值回到了之前的状态,戳记值也会不同,从而让CAS操作能够识别出中间的变化。
它的核心构造方法如下:
// 构造方法,传入初始引用和初始戳记
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
核心方法说明
AtomicStampedReference提供了几个核心方法用于操作引用和戳记,具体说明如下:
| 方法名 | 作用说明 |
|---|---|
| getReference() | 获取当前的引用值 |
| getStamp() | 获取当前的戳记值 |
| get(int[] stampHolder) | 同时获取引用值和戳记值,戳记值会被存入传入的数组中 |
| compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) | 如果当前引用等于预期引用,且当前戳记等于预期戳记,就将引用更新为新引用,戳记更新为新戳记,返回是否更新成功 |
| attemptStamp(V expectedReference, int newStamp) | 如果当前引用等于预期引用,就将戳记更新为新戳记,引用不变 |
实际使用案例
下面通过一个简单的示例演示AtomicStampedReference如何解决ABA问题导致的状态判断失真。假设我们有一个账户余额的场景,初始余额是100,戳记为0,线程1要将余额改成200,线程2在中间将余额改成150再改回100,我们看AtomicStampedReference如何识别这个变化。
import java.util.concurrent.atomic.AtomicStampedReference;
public class AtomicStampedReferenceDemo {
// 初始引用为100,初始戳记为0
private static AtomicStampedReference<Integer> balance = new AtomicStampedReference<>(100, 0);
public static void main(String[] args) throws InterruptedException {
// 线程1:预期将余额从100改成200
Thread thread1 = new Thread(() -> {
int[] stampHolder = new int[1];
Integer currentRef = balance.get(stampHolder);
int currentStamp = stampHolder[0];
System.out.println("线程1获取初始余额:" + currentRef + ",初始戳记:" + currentStamp);
try {
// 模拟线程1执行耗时,让线程2先完成ABA操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 尝试更新,预期引用100,预期戳记0,新引用200,新戳记1
boolean success = balance.compareAndSet(currentRef, 200, currentStamp, currentStamp + 1);
System.out.println("线程1更新余额结果:" + success + ",当前余额:" + balance.getReference() + ",当前戳记:" + balance.getStamp());
});
// 线程2:执行ABA操作,100->150->100
Thread thread2 = new Thread(() -> {
int[] stampHolder = new int[1];
// 第一次修改:100改成150,戳记从0改成1
Integer currentRef = balance.get(stampHolder);
balance.compareAndSet(currentRef, 150, stampHolder[0], stampHolder[0] + 1);
System.out.println("线程2第一次修改后余额:" + balance.getReference() + ",戳记:" + balance.getStamp());
// 第二次修改:150改回100,戳记从1改成2
currentRef = balance.get(stampHolder);
balance.compareAndSet(currentRef, 100, stampHolder[0], stampHolder[0] + 1);
System.out.println("线程2第二次修改后余额:" + balance.getReference() + ",戳记:" + balance.getStamp());
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
运行上述代码后,线程1的更新操作会失败,因为虽然余额回到了100,但是戳记已经从0变成了2,和线程1预期的戳记0不匹配,CAS操作会识别出中间的变化,避免了状态判断失真。
适用场景说明
AtomicStampedReference适合用在需要感知引用中间变化的高并发场景,比如版本控制、状态流转需要记录变更次数的场景。如果只需要判断引用是否被修改过,不需要关心修改次数,也可以使用AtomicMarkableReference,它用布尔类型的标记代替整型戳记,更轻量。需要注意的是,戳记变量需要合理设计递增逻辑,避免溢出问题,通常高并发场景下戳记递增的速度不会达到整型溢出的阈值,如有特殊场景可以考虑使用长整型存储戳记。
AtomicStampedReference戳记变量高并发状态判断失真CAS修改时间:2026-07-16 03:24:29