什么是CAS :
Compare and Swap,即比较再交换。在没有锁的情况下,能够保证多个线程对一个值的更新
CAS有3个操作数:内存值V、预期值A、要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。该操作是一个原子操作,被广泛的应用在Java的底层实现中。在Java中,CAS主要是由sun.misc.Unsafe这个类通过JNI调用CPU底层指令实现
执行流程如下:
有一个值 E=0, 对他进行加1 操作,一个线程读到E=0 后加1 ,计算后结果值v=1 ,
接下来比较E和E的当前最新值N(若有其他线程修改E记为N),如果相等就说明其他线程没有修改过E的值,更新E为最新的值V ,如果不相等,说明其他线程已经修改过E的值 比如说N=2,这时会重新读取E 的值,在计算结果值V=2+1,v=3后,在比较E和N 的值,相等更新不相等继续循环。
ABA问题
0 改为1 时回写时,其他线程把E的值从0改为2后又改为0,这样发现E的值还是0,其实已经发生过改变 。
乐观锁版本号
AtomicStampReference
读的时候不仅读取值还读取版本号。任何线程修改E的值的时候,都增加版本号,比较的时候,不仅比较值还比较版本号是否相同。
CAS源码实现如下:
AtomicInteger i = new AtomicInteger();
i.incrementAndGet();
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
可以看到是调用unfase 类的 getAndAddInt 方法,在往里面看
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
调用了一个方法compareAndSwapInt 这就是cas 操作的方法,在往里面看
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
调用了一个native 修饰的方法,native 修饰的是C/C++ 写的代码
hotspot 里面unsafe实现
调用了 Atimic::cmpxchg 方法
该方法的实现是 LOCK_IF_MP cmpxchg
LOCK_IF_MP: lock if Multi Processor 如果多个cpu
cmpxchg : compare and exchange 比较并交换
最终实现
lock cmpxchg 指令
lock 保证原子性是硬件级别的,cpu 在改变值的时候,其他的cpu不能对其进行修改
cpu 如何实现原子性
- 总线锁:当一个线程操作共享变量时,在bus 总线上发出Lock信号,其他线程就不能操作这个变量了
- 缓存锁:MESI 缓存一致性协议
CAS 优点
保证变量原子性
并发不高,时间不长的情况下,比锁效率高