CAS
CAS(Compare And Swap),是Java中的一种无锁优化,又被称作自旋锁或者乐观锁,而synchronized和ReentrantLock则是悲观锁的典型代表
乐观锁:总是假设最好的情况,每次拿数据都认为别人不会修改数据,所以不会加锁,但是更新的时候,会判断在此期间有没有人修改过;一般基于版本号机制实现
悲观锁:总是假设最坏的情况,每次拿数据都认为别人会修改数据,所以要加锁,别人只能等待,直到我释放锁之后别人才能拿到锁
原子类
在Java中的各种原子类的递增递减等方法都是使用的CAS机制保证的数据的原子操作,从而保证数据的一致性,最终调用的都是Unsafe类中的方法,这里拿AtomicInteger的incrementAndGet方法来做示例:
//AtomicInteger中的incrementAndGet方法
public final int incrementAndGet() {
//因为Unsafe类中的getAndAddInt方法返回的都是期望值,也就是当前值,但此时已经内部完成了自增操作,所以这里需要对返回值进行加一后返回
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
//Unsafe中的getAndAddInt方法
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));//执行调用JNI方法(CAS)来完成操作,如果失败则循环执行
return var5; //返回期望值
}
CAS原理图
CAS原理的伪代码如下所示
CAS(Value,Except,NewValue)
if Value==Except
Value=NewValue
otherwise try again or fail
细心的朋友到这里可能会问,如果在一个线程判断完Value==Expect,但是未完成Value=NewValue时Expect被其他线程修改了怎么办?
这里完全是不用担心的,这是受底层CPU支持的原子操作,一旦执行就不能被其他线程打断,即Value==Expect和Value=NewValue要么同时完成,要么同时失败
ABA问题
CAS是一种乐观锁,在运行过程中假定其他线程不会来修改数据,但在高并发的环境下,难免会遇到数据被修改的情况,如果数据被修改的和原来不一样了,那么没有关系,会被检测到重新获取一次期望值再次执行即可,但是如果数据被多次修改后最终变回原值,这对于一般的CAS就没法检测了,这就是ABA问题,当数据时基本类型时,该问题并无大碍,但如果数据时引用类型时就有可能出问题,该处假定一个场景:期望值为对象A,在其他线程中把期望值设置为B,并修改了B中的值,最后将期望值又设置为对象A,这时就可能会出现问题了
解决方法:可以在期望值对象中增加一个版本号,每当其中的值被修改时,就将版本号加一,在CAS的判断中不止判断期望值的值,而且还要判断期望值的版本号从而规避ABA问题
如果对你有帮助,点个赞,或者打个赏吧,嘿嘿
整理不易,请尊重博主的劳动成果