一、Cas的功能:
Cas全称为compare and swap,翻译过来就是比较并且交换,cas的三个操作数为:
V 需要改变的地址
A 期望的V指向的地址的值
B 将会和V中数值交换的值
可以简单的将cas理解为一个有三个参数的方法boolean cas(V,A,B);
当V和A的值相等时,就将V和B的值进行交换。并且返回true,V和A不相等时,cas就什么都不做直接返回false。且上面的操作是原子的。
二、Cas的应用
(1)实现自旋锁
自旋锁在锁竞争时,需要一直循环查看锁是否被释放,释放就一直循环检测,释放了就马上获得锁。使用cas可以实现这种自旋的循环条件。
将V设置为锁的主人,A设置为空,B设置为需要获得锁的线程,此时将!cas作为while的循环条件,只有当cas返回true时,循环才会结束,线程就获得了锁,否则cas返回false,while循环就一直循环。
(2)实现原子类
原子类是一类线程安全类,原子类的加减乘除是线程安全的,不用加锁,实现原子类也能使用cas。比如使用cas将自增变得线程安全。
int value;
public void atomicAdd(){
int oldvalue=value;
while(!cas(value,oldValue,oldValue+1)){
//value和oldValue相等,交换,然后返回true,跳出循环,不相等返回false,进入循环
oldValue=value;//更新oldValue的值
}
//不管value的值是否被改变,这个方法就是为了让value的值+1
}
二、ABA问题
cas比较的原数据和被记录下的数据的大小来判断原数据是否在被记录下来之后发生了改变, 但是如果原数据A被记录下来后,先被改变成了B,然后又被改回了A,此时cas还是认为原数据A没有被改变,会直接执行交换的操作。 但是实际上A此时已经被改变了,按照逻辑,cas应该返回false,不执行任何操作。这就是ABA问题。
解决ABA问题的方法;
版本号法:为每个原数据都设置一个版本号,原数据改变一次,版本号就递增,版本号永远不会重复,此时根据版本号来比较数据是否改变,就不会出现ABA问题。