一、什么是CAS
CAS(Compare-and-Swap)顾名思义,叫做比较和交换,大概执行过程是什么样呢?假设现有两个寄存器A、B和一个内存M,通过对寄存器A中的值与内存M的值进行比较是否相同,来决定寄存器B中的值是否与内存中M的值进行交换,若不相同则不会发生什么,若相同则将寄存器B的值与内存M的值进行交换;
CAS的伪代码:
class AtomicInteger {
private int value;
public int getAndIncrement() {
int oldValue = value;
while ( CAS(value, oldValue, oldValue+1) != true) {
oldValue = value;
}
return oldValue;
}
}
- oldValue相当于寄存器A,int oldValue = value是把内存value的值读取到读取到寄存器A中;
- getAndIncrement()这个方法相当于count++;(count相当于计数器)
- oldValue + 1也把可以理解为是另一个寄存器B的值;
- while循环是干什么的?通过比较value这个内存中的值,是否和寄存器A中的值相等,若相同,就把交换寄存器B和内存M的值(着重点是:寄存器B的值,设置到value中),同时返回true结束循环;如果不相同,CAS返回false,进入循环体中,重新读取内存value的值,放入寄存器A中;
总结:CAS是CPU的一条指令,原子性完成的,线程安全,效率也很高;
二、CAS的应用——实现自旋锁
自旋锁原理:当锁被其他线程拥有时,另外的线程不会挂起等待,而是会反复询问,看当前锁是否被释放,整个过程为纯用户态的轻量级锁;
伪代码:(注释详情)
public class SpinLock {
private Thread owner = null;//表示当前锁是谁的
public void lock(){
while(!CAS(this.owner, null, Thread.currentThread())){
//比较owner的值与null是否相同(null表示解锁状态),若owner的值为null
//就把当前调用lock的线程的值设置到owner中(成功加锁),并结束循环;
//若owner不为null,则CAS不进行交换,返回false,进入循环,再次进入判断;
}
}
public void unlock (){
this.owner = null;
}
}
三、ABA问题
什么是ABA问题?
内存中的值由A变为B,又变回A,CAS进行内存与寄存器值比较时,并未察觉变化;
CAS中出现ABA问题,大多数情况下不会有问题,但极端情况下也可能出现问题,举个栗子:假设你去自动取款机中取钱,你的账户里有1000元,你准备从卡里去除500元,但是由于取款卡了一下,你以为自己没点上,所以又多点了一下,此时取款机就创建出两个线程都基于CAS来进行扣款操作,正好凑巧,此时你的好友也往你的账户上转了500元,于是等取款机反应过来,机子上显示的剩余存款的数据还是500元;以上情形就导致了多扣了一次500元,也就是极端情况下的BUG;
如何解决ABA问题?
可以通过一个记录,来保存内存的修改次数,或者是记录上一次的修改时间;
这时,就可以通过比较查看上一次修改的时间或者是修改次数,来观察到是否出现了ABA问题导致的BUG;