目录
[CAS 的应用场景](#CAS 的应用场景)
[CAS 的优劣点](#CAS 的优劣点)
原理
Compare And Swap(比较再交换):在更新前检查数据是否被修改,若未被修改,则更新,否则自旋重试
具体解释
主存中有一个共享变量,当线程想要去修改这个变量的值的时候,需要先把这个变量的值拷贝到线程的工作内存中,然后进行计算操作,得到一个新的值。然后线程带着旧值和新值再次找到主存中的共享变量。
先比较旧值和现在主存中的共享变量是否相同。
-
若相同,说明在这期间没有其他线程修改过主存中的共享变量,是线程安全的。因此把新值写入主存的共享变量中。
-
若不同,说明在这期间有其他线程修改过主存中的共享变量,不是线程安全的。因此此时需要自旋重试
- 自旋:再次取得主存中共享变量的值。回到线程的工作内存进行计算操作得到新值。再次比较旧值和主存中的共享变量的值是否相同。又回到刚刚的逻辑。直至自旋成功。
CAS 伪代码实现
java
class AtomicInteger { //返回自增前的值
private int value; // 共享变量,在主存中
public int getAndIncrement() {
int oldValue = value; // 第一次读取主存中的值到线程工作内存,相当于这就就是"旧值"
while (CAS(value, oldValue, oldValue + 1) != true) {
// 线程带着"旧值":oldvalue和"新值"oldValue+1,用"旧值"和主存中的共享变量作比较
oldValue = value; // 如果失败,说明值被其他线程改了,重新读取最新值
}
return oldValue; // 返回的是更新前的旧值
}
}
CAS 的应用场景
-
AQS中大量使用到CAS来实现基本操作。
-
JUC包下的原子类中(例如AtomicInteger) 大量使用到CAS + volatile来保证线程安全。
CAS 的优劣点
优点:CAS是一种乐观锁,不加锁,无锁并发保存线程安全。
缺点:
-
自旋占用CPU
-
可能会导致ABA问题
ABA问题
在做CAS操作时
-
线程1获取值为A
-
线程2修改值为B
-
线程3修改值为A
-
线程2获取值还是为A,以为该值从未被修改过,这就是ABA问题
解决ABA问题的方式
思路:使用带有版本号的CAS(比如使用AtomicStampedReference类)
-
该类的原理是引入一个版本号,每次操作,都会给版本号一个新值。进行CAS操作时,不仅要求数值要和之前一样,版本号也要一样。
-
当数值一样,但是版本号不同时,说明发生了ABA问题,此时值不会被修改