CAS简介
CAS(compare and swap)比较交换,原子操作的一种,用于多线程环境下保持数据一致性。CAS有三个操作数,内存值V,旧的预期值A,要修改的新值B,该操作将
V与A进行比较,如果一样,就将B赋值给V,如果不一样就操作失败。
使用场景
常用替换锁实现对数据的修改操作,如果成功返回,可以选择重试或者其他方法,java中的AtomicInteger类用到了CAS
// AtomicInter method of getAndSet
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
// Unsafe method of getAndSetInt
public final int getAndSetInt(Object o, long offset, int newValue) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, newValue));
return v;
}
存在的问题
ABA问题
进程P1读取了一个数值A,P1被挂起,进程P2开始执行,P2将B修改为A,对于P1来说已经修改过了,继续使用可能会出现问题。需要对其添加版本信息。
自旋锁提出的背景
在多处理系统下,有些资源需要互斥访问,这就引入了锁机制,只有获得了锁,然后才能对资源进行访问,同一时间只能
有一个线程能得到锁,进入临界区。对于其他线程,可以是不断的循环等待,观察锁是否释放,它不用把线程阻塞,这就是自旋锁。
另外一种就是没有获得锁就阻塞自己,请求CPU调度别的线程,互斥锁
自旋锁的原理
和互斥锁类似,自旋锁是一种比较低级的保护数据结构或者代码片段的原始方式,会出现递归死锁和占用很多的CPU资源
递归死锁
试图递归地获得自旋锁必然会引起死锁:递归程序的持有实例在第二个实例循环,以试图获得相同自旋锁时,不会释放此自旋锁。在递归程序中使用自旋锁应遵守下列策略
:递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。
此外如果一个进程已经将资源锁定,那么,即使其它申请这个资源的进程不停地疯狂“自旋”,也无法获得资源,从而进入死循环。
占用很多的CPU资源
由于线程一直在循环等待,必定会造成CPU使用很高,通常情况下,会有一个尝试的限制,如果循环超过了限制,就挂起
使用场景
自旋锁比较锁使用者保持锁时间很短的情况,jdk1.7中的concurrentHashMap中put方法使用了自旋锁。
参考链接
https://zh.wikipedia.org/wiki/%E6%AF%94%E8%BE%83%E5%B9%B6%E4%BA%A4%E6%8D%A2
https://liuzhengyang.github.io/2017/05/11/cas/
领取专属 10元无门槛券
私享最新 技术干货