CAS是什么:
CAS是CPU并发原语,由于CAS是系统原语,由若干条指令组成,而且原语在执行的过程中必须是一致的,所以CAS是线程安全的。CAS算法是一种无锁算法,但是CAS可以理解成不同于synchronouse的乐观锁。
CAS基本原理:
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
//代码模拟简易过程
AtomicInteger atomicInteger = new AtomicInteger(10);
// 10为期望值(A),1为新值(B)
// 将主内存的值修改为1 修改成功,主内存值为1(V)
atomicInteger.compareAndSet(10, 1)
// 主内存的值为1 ,但是当前期望值是10,那么V和A值不同,修改失败
atomicInteger.compareAndSet(10, 2)
CAS底层原理:
CAS会把当前工作内存中的值和主内存中的值相比较,如果相同则执行后续操作,否者继续自旋偏移取主内存的值比较,直到主内存和工作内存的值一致为止。
基于jdk1.8.0_20 部分源码
//Unsafe是CAS的核心类,Java无法操作底层系统,Unsafe就可以操作底层(Unsafe内部方法由native修饰)
//此方法块的意思就是获取value值在工作内存中的偏移量valueOffset
private static final Unsafe unsafe = Unsafe.getUnsafe();
static {
try {
//假如线程1和线程2都从主内存获取的变量值为10,先通过此方法获取10在工作内存的偏移量
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//通过valueOffset,直接通过内存地址,获取到值,然后把主内存值加20
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 20);
}
//var1为this,AtomicInteger对象本身
//var2为valueOffset
//var4为要增加的值
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
//通过对象和valueOffset拿到主内存中真实的值(var5)
var5 = this.getIntVolatile(var1, var2);
//执行compareAndSwapInt比较工作内存中的值和主内存的值是否相等
//如果主内存中的值(var5)=工作内存的值value,则将值设为var5+var4,并返回true,否则返回false
//假如线程1执行完毕了,返回了true,那么主内存的值就变成了30,那么线程2的工作内存的值还是10,而主内存是30,显然他们不相等,就返回false,执行执行do-while循环重新获取主内存的值
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
CAS缺点:
ABA问题(CAS每次更新主内存的值之前都会拿之前从主内存取的值A和现在从主内存取的值B相比较,如果一致就更新,不一致就不更新,可以通过增加版本号解决此问题)
循环时间长,开销大(它的自旋会导致开销大)
无法对多个共享变量保证原子性,只能对一个共享变量(对多个共享变量就要加锁了)