CAS原理

CAS指令和具体源代码

源码说话,咱们看一下getAndIncrement方法:java

 1 //该方法功能是Interger类型加1
 2 public final int getAndIncrement() {
 3     //主要看这个getAndAddInt方法
 4     return unsafe.getAndAddInt(this, valueOffset, 1);
 5 }
 6 
 7 //var1 是this指针
 8 //var2 是地址偏移量
 9 //var4 是自增的数值,是自增1仍是自增N
10 public final int getAndAddInt(Object var1, long var2, int var4) {
11     int var5;
12     do {
13       //获取内存值,这是内存值已是旧的,假设咱们称做指望值E
14        var5 = this.getIntVolatile(var1, var2);
15        //compareAndSwapInt方法是重点,
16        //var5是指望值,var5 + var4是要更新的值
17        //这个操做就是调用CAS的JNI,每一个线程将本身内存里的内存值M
18        //与var5指望值E做比较,若是相同将内存值M更新为var5 + var4,不然作自旋操做
19     } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
20 
21     return var5;
22 }
解释一下getAndAddInt方法的流程:

假设有一下情景:多线程

  1. A、B两个线程
  2. jvm主内存的值1,A、B工做内存的值为1(工做内存会拷贝一份主内存的值)
  3. 当前指望值为1,作加1操做
  4. 此时var5 = 1,var4 = 1;
    1. A线程将var5与工做内存值M比较,比较var5是否等于1
    2. 若是相同则将工做内存值修改成var5 + var4 即修改成2并同步到驻村,此时this + valueOffset指针里,示例变量value的值就是2,结束循环
    3. 若是不相同,则是B线程修改了主内存的值,说明B线程已经先于A线程作了加1操做,A线程没有更新成功须要继续循环,注意此时var5更新为新的内存值,假设当前的内存值是2,那么此时var5 = 2,var + var4 = 3,重复上述步骤直到成功(自旋),成功以后,内存地址中的值就改变为3

简单来讲,就是将工做内存的值与主内存的值来进行比较,若是相等,说明没有被其余线程修改,则进行赋新值操做,若是不相等,说明主内存的数据被其余线程更改过,则此时须要更新工做内存的副本值,而且从新获取主内存的值,再次进行比较,直到两者相等为止;jvm

CAS优缺点

  • 优势

非阻塞的轻量级的乐观锁,经过CPU指令实现,在资源竞争不激烈的状况下性能高,相比synchronized重量锁,synchronized会进行比较复杂的加锁、解锁和唤醒操做。性能

  • 缺点
  1. ABA问题: 线程C、D;线程D将A修改成B后又修改成A,此时C线程觉得A没有改变过,java的原子类AtomicStampedReference,经过控制变量值的版本号来保证CAS的正确性。具体解决思路就是在变量前追加上版本号,每次变量更新的时候把版本号加一,那么A - B - A就会变成1A - 2B - 3A。
  1. 自旋时间过长,消耗CPU资源,若是资源竞争激烈,多线程自旋长时间消耗资源
相关文章
相关标签/搜索