CAS 即 Compare and swap。比较并交换,是一种很重要的同步思想。如果主内存的值跟期望值一样,那么就进行修改,否则一直重试,直到一致为止。
上一节当中的原子性问题,就可以基于CAS思想进行解决。如下
public class CasDemo {
private AtomicInteger num=new AtomicInteger();
public static void main(String[] args) {
CasDemo demo = new CasDemo();
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
demo.add();
}, "MyThread" + i).start();
}
System.out.println("num=" + demo.num);
}
public void add() {
num.getAndAdd(1);
}
}
这里借助了原子类AtomicInteger
举例:
public static void main(String[] args) {
AtomicInteger num=new AtomicInteger(0);
//如果num的当前值是0 则把它更新为1
System.out.println(num.compareAndSet(0, 1));
//如果num的当前值是0 则把它更新为2(实际上这条语句就更新失败,因为num当前值不是0了)
System.out.println(num.compareAndSet(0, 2));
//打印num当前值
System.out.println(num.get());
}
结果:
true
false
1
底层原理分析
以AtomicInteger#getAndIncrement
方法为例分析
AtomicInteger
内部维护了volatile int value
和private static final Unsafe unsafe
两个比较重要的参数。
getAndIncrement
方法调用了Unsafe.getAndAddInt
。Unsafe
类的大部分方法都是native
的,用来像C语言一样从底层操作内存。
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和var2 分别表示对象和对象在内存中的偏移量。根据对象和偏移量得到在主内存的快照值var5。然后compareAndSwapInt
方法通过var1和var2得到当前主内存的实际值。如果这个实际值跟快照值相等,那么就更新主内存的值为var5+var4。如果不等,那么就一直循环,一直获取快照,一直对比,直到实际值和快照值相等为止。
因此 可以说CAS实际上是一种自旋锁,这种方式虽然轻量,但是也会存在问题:
- 一直循环,开销比较大
- 只能保证一个变量的原子性操作,如果有多个变量的话依然要加锁
- 存在ABA问题