CAS是什么?
CAS表示比较并交换,它是一条cpu并发原语指令。
我们在多线程开发环境当中不免会使用到AtomicInteger类来保证多线程安全性
例如:
AtomicInteger atomicInteger = new AtomicInteger(3);
atomicInteger.getAndIncrement();
点进去getAndIncrement()方法里面会有
public final int getAndIncrement() { //valueOffset是内存偏移量 内存地址
return unsafe.getAndAddInt(this, valueOffset, 1);
}
调用CAS的unsafe类中的方法 JVM会实现汇编指令,这种指令是依赖于一种硬件的操作,通过它来实现原子操作
以unsafe.getAndAddInt(this, valueOffset, 1);为例
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {//自旋
var5 = this.getIntVolatile(var1, var2); //先从内存中拿当前对象的快照
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));//比较并交换
return var5;
}
代码解释:
var1 AtomicInteger对象本身
var2 当前对象在内存里面的偏移量(引用地址)
var4 需要变动的量,此处为1
var5 是通过var1 var2在主内存里面的真实的值
先从内存中拿到快照(var5) , 比较并交换时:先查看var1对象通过var2地址在内存中的真实值 与期望值进行比较,如果期望值没有被修改则比较交换完成, 最终值为var5+var4
以代码为例子以更好的体会CAS
假设有两个线程A、B
1.AtomicInteger里面的value原始值为3,即主内存中AtomicInteger的value为3,根据JMM模型,线程A和线程B各自持有一份值为3的value的副本分别到各自的工作内存.
2.线程A通过getIntVolatile(var1,var2) 拿到value值3,这时线程A被挂起.
3.线程B也通过getIntVolatile(var1,var2) 拿到value值3,此时刚好线程B没有被挂起并执行compareAndSwapInt方法比较内存中的值也是3 成功修改内存的值为4 线程B打完收工 一切OK.
4.这时线程A恢复,执行compareAndSwapInt方法比较,发现自己手里的数值和内存中的数字4不一致,说明该值已经被其他线程抢先一步修改了,那A线程修改失败,只能重新来一遍了.
5.线程A重新获取value值,因为变量value是volatile修饰,所以其他线程对他的修改,线程A总是能够看到,线程A继续执行compareAndSwapInt方法进行比较替换,直到成功.
CAS的缺点
1.cas失败会一直在尝试,如果cas长时间不成功会给cpu带来很大的开销
2.只能保证一个共享变量的原子操作
3.这样可能会导致ABA问题