CAS
一种在操作系统层实现的并发算法。Java中的原子包大量使用了该算法来简化多线程编程。
当前内存值V 预期原值A 预期目标值B。当V==A时,将B赋值给变量,返回true,否则什么都不做,返false。
在Java中,通过UnSafe类对操作系统底层的CAS进行封装。以AtomicInteger为例子,对源码实现进行分析;
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates 使用UnSafe类来调用compareAndSwapInt方法,对变量进行原子更新操作。
private static final Unsafe unsafe = Unsafe.getUnsafe();
// 变量内存偏移地址 UnSafe通过内存偏移地址来获取变量的值。
private static final long valueOffset;
static {
try {
// 初始化时获取变量内存偏移地址
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
// 变量的值 volatile修饰 保证看到的总是最新值 即多线程可见性
private volatile int value;
// 对addAndGet方法进行分析 入参为增量
public final int addAndGet(int delta) {
// 自旋操作 直到成功
for (;;) {
// 获取原值 value
int current = get();
// 预期目标值=原值+增量
int next = current + delta;
// 原子操作 将预期原值 和预期目标值传入
if (compareAndSet(current, next))
return next;
}
}
public final boolean compareAndSet(int expect, int update) {
// UnSafe类参数为 变量引用 内存偏移量 预期原值 预期目标值 该方法为native方法 实现为:通过内存偏移量获取内存中的值和预期原值比较,两者相等则将内存值设为预期目标值并返回true,否则什么都不做,返回false
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
ABA问题
两个线程X Y Z同时对一个原子AtomicInteger 进行操作,原值为A。线程X Z读取到最新值均为A,线程X先将值改为B成功。然后线程Y读取到最新值为B,又将值改为A,线程Z在进行CAS操作时,感知不到线程X进行过修改,这就是ABA问题。
场景分析,一个单项链表实现的栈,原本
针对ABA问题,提供了AtomicStampedReference原子引用类,通过控制变量版本号保证CAS的正确性。