CAS
全名 compare and swap
CAS 解决了什么问题?
public class{
public static void main(String[] args){
new Thread(() -> {
Counter.count++;
}, "thread-1").start();
new Thread(() -> {
Counter.count++;
}, "thread-2").start();
}
public static class Counter{
public static int count = 0;
}
}
Counter.count++ 包括三个步骤
- 读取 Counter.count 的值
- 计算 Counter.count 加 1 后的值
- Counter.count 赋值为计算后的值
以下是有可能出现的执行顺序
可以看到,进行了两次 +1 但是从结果来看只加了一次
究其原因,是因为操作的值,在我们读之后,写之前,这个空窗期产生了变化,但是这个变化被忽视了
CAS 怎么解决的问题
首先,问题的产生是因为,忽视了空窗期值的变化,那不忽略就得了呗,最终赋值的时候检查下变没变
Unsafe 中的 cas 方法
// var1 是要修改的对象
// var2 是要修改的变量的内存偏移量,由 var1 和 var2 可以得到变量的内存地址
// var4 要加上的值
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));
// compareAndSwapInt native 方法,成功返回 true
// var1 + var2 可以得到变量的最新值,
// 然后和旧值(var5) 比较,如果相等,把计算后的值(var5 + var4)写入到变量的内存地址中
return var5;
}
ABA 问题
由于我们只是比较值是否和读的时候一致,那就有可能读到 A 之后,然后变为 B ,在比较的时候又变成 A,
这个问题的解决方法是给数据加上版本号,不仅比较值,还比较版本号。
数据的值 | 数据的值 |
---|---|
A | 1.0 |
B | 2.0 |
A | 3.0 |
最后
cas 的优点是避免线程阻塞唤醒的开销,但是在极端情况下,会一直自旋(一直在while中循环),适合写少读多的情况