一. CAS定义
1. CAS:比较和交换(Conmpare And Swap)是用于实现多线程同步的原子指令。
2. 它将内存位置的内容与给定值进行比较,只有在相同的情况下,将该内存位置的内容修改为新的给定值。
3. 这是作为单个原子操作完成的。
4. 原子性保证新值基于最新信息计算; 如果该值在同一时间被另一个线程更新,则写入将失败。
5. 操作结果必须说明是否进行替换; 这可以通过一个简单的布尔响应(这个变体通常称为比较和设置),或通过返回从内存位置读取的值来完成(摘自维基本科)
二. 简单案例演示
public static void main(String[] args) {
//主物理内存设置为 3
AtomicInteger atomicInteger = new AtomicInteger(3); //默认是 0
//main线程打算在工作内存修改值为 2020
System.out.println(atomicInteger.compareAndSet(3, 2020)+"\t current data: " + atomicInteger.get());
//main线程打算在工作内存修改值为 1024
System.out.println(atomicInteger.compareAndSet(3, 1024)+"\t current data: " + atomicInteger.get());
}
运行结果:
结果解析
为什么会出现上面的结果呢?
1. 因为compareAndSet(?,?)方法第一个参数代表的是期望值,
这个值负责和主内存也就是我们定义的3比较,如果相同,就返回true,并且把主存的值给交换掉。
所以会在控制台打印第一句话: true current data: 2020
3. 因为现在主内存的值已经被更改为2020,
所以3与之比对失败,返回false,输出主内存的值
这就是控制台打印的第二句话:false current data: 2020
三. CAS的底层原理
我们可以看一下其底层代码
以atomicInteger.getAndIncrement();方法为例进入底层
进入一层是AtomicInteger类
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
在下一层是Unsafe类
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 AtomicInteger对象本身
var2 该对象的引用地址
var4 需要变动的数据
var5 通过var1 var2找出的主内存中真实的值用该对象前的值与var5比较;
如果相同,更新var5+var4并且返回true,退出循环。
如果不同,继续循环取值然后再比较,直到更新完成。
var5 = this.getIntVolatile(var1, var2); 相当于从主内存中取出值复制副本到工作内存。
this.compareAndSwapInt(var1, var2, var5, var5 + var4) 相当于是主内存的值与工作内存的值比较,相同,则改变值。
CAS的缺点
1.** 循环时间长,开销大**
例如getAndAddInt方法执行,有个do while循环,
如果CAS失败,一直会进行尝试,如果CAS长时间不成功,
可能会给CPU带来很大的开销
2.只能保证一个共享变量的原子操作
对多个共享变量操作时,循环CAS就无法保证操作的原子性,
这个时候,就可以用锁来保证原子性
3.可能会引发ABA问题