CAS是实现并发计算时常用到的技术,是一条CPU的原子指令,不会存在线程安全问题
CAS操作包含三个操作数:内存值V,预期值A,更新值B。当且仅当预期值A和内存值V相等时,才会将内存值V修改为更新值B。否则不做任何操作,多线程同时执行CAS操作只有一个会成功
Unsafe是CAS的核心类,由于Java无法直接访问底层系统,需要通过Native方法来访问,因此Java中的CAS操作的执行都依赖于Unsafe类的方法
AtomicInteger
public class AtomicInteger extends Number implements Serializable {
private static final long serialVersionUID = 6214790243416807050L;
private static final Unsafe unsafe = Unsafe.getUnsafe();//初始化Unsafe类
private static final long valueOffset;//value成员变量的内存偏移量
private volatile int value;//当前value值
static {
try {
//初始化valueOffset
valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception var1) {
throw new Error(var1);
}
}
}
AtomicInteger中的getAndIncrement()
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
Unsafe中的getAndAddInt()
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
//根据当前对象this和内存偏移量valueOffset获取变量的当前内存值
var5 = this.getIntVolatile(var1, var2);
//当且仅当预期值和内存值相等时,将内存值修改为更新值,否则一直自旋
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
CAS两大缺点
1.循环时间长开销大:自旋CAS如果长时间不成功,会给CPU带来很大的开销
2.ABA问题:线程1读取到的内存值是A,线程2读取到的内存值也是A;当线程2将内存值修改为B,再将内存值修改为A;线程1修改内存值的时候发现内存中仍然是A,符合预期。尽管在某些业务中只要前后值一样,对最终的结果就不会有影响;但在某些特殊的业务中变量在中间过程是不能被修改的,因此可以使用版本号机制解决ABA问题。