CAS很好理解
CAS的实现
compareAndSwap()通过三个参数 V,E,N 实现,V是要更新的变量,E是预期值,N是新值。当V的值等于E值时,将V的值设为N,否则说明存在已经被其他线程更新。
compareAndSwap()是native方法,底层实现中,在多核CPU环境下,会增加一个Lock指令对缓存或总线加锁,从而保证比较并替换这两个指令的原子性。这里注意变量要加volatile。
ABA问题
- 线程 T1 读取到某个变量 A 的值。
- 在 T1 进行下一步操作之前,线程 T2 将该变量的值从 A 改为 B,然后又改回 A。
- 线程 T1 此时仍然认为变量的值没有被其他线程修改过(因为它看到的值仍然是 A),于是继续执行。
如何解决ABA问题?
增加版本号
使用一个附加的版本号,每次更新变量时同时更新版本号。通过比较版本号而非单纯比较值来判断变量是否被修改。
使用更高层次的锁
使用ReentrantLock或者synchronized更高级别的锁代替CAS。
使用AtomicStampedReference替代
AtomicStampedReference 是 Java 并发包(java.util.concurrent.atomic 包)中的一个类,用于解决 ABA 问题。它通过引入一个额外的整数 "戳" (stamp)来跟踪引用的版本,每次更新引用时同时更新戳,从而避免仅通过引用判断是否发生变化所导致的 ABA 问题。
public static void main(String[] args) {String initialRef = "initial value";int initialStamp = 0;AtomicStampedReference<String> atomicStampedRef = new AtomicStampedReference<>(initialRef, initialStamp);// 获取当前引用值和戳int[] stampHolder = new int[1];String currentRef = atomicStampedRef.get(stampHolder);int currentStamp = stampHolder[0];System.out.println("Initial: Reference = " + currentRef + ", Stamp = " + currentStamp);// 模拟 ABA 问题atomicStampedRef.compareAndSet(currentRef, "new value", currentStamp, currentStamp + 1);atomicStampedRef.compareAndSet("new value", initialRef, currentStamp + 1, currentStamp + 2);// 尝试使用旧的引用和戳更新boolean isUpdated = atomicStampedRef.compareAndSet(currentRef, "another new value", currentStamp, currentStamp + 1);System.out.println("Update succeeded: " + isUpdated); // Output will be falseSystem.out.println("After update: Reference = " + atomicStampedRef.getReference() + ", Stamp = " + atomicStampedRef.getStamp());// 再次获取当前引用值和戳currentRef = atomicStampedRef.get(stampHolder);currentStamp = stampHolder[0];System.out.println("Final: Reference = " + currentRef + ", Stamp = " + currentStamp);
}