java多线程---CAS实现原理

   一,CAS的使用demo

    我们通过一个demo来体会cas的功能是什么:

public class MyText{
      //这里相当于两个integer 数字,一个是原子的,一个是普通的,我们通过++,来比较最后的值。
      private AtomicInteger atomicI=new AtomicInteger(0);
      private int i=0;
      //使用CAS实现线程安全计数器
      private void safeCount() {
            for(;;) {
                  int ia=atomicI.get();
                  boolean suc=atomicI.compareAndSet(ia, ++ia);
                  if(suc)
                        break;
            }
      }

      //非线程安全计数器
      private void count() {
            i++;
      }

      public static void main(String []args) throws InterruptedException  {
            final MyText text=new MyText();
            List<Thread> ts=new ArrayList<Thread>(600);
            for(int j=0;j<100;j++) {
                  Thread t=new Thread(new Runnable() {
                        public void run() {
                              for(int i=0;i<10000;i++) {
                                    text.count();
                                    text.safeCount();
                              }
                        }
                  });

                  ts.add(t);
            }

            for(Thread t:ts)
                  t.start();  //启动所以线程
            //等待所以线程执行完成才输出结果;
            for(Thread t:ts) {
                  t.join();
            }

            System.out.println("未上锁:"+text.i);
            System.out.println("原子操作:"+text.atomicI.get());
      }
}

结果: 可见两种结果相差甚远,现在你也直到了cas是做什么的了吧,它就是代替锁去实现同步操作的。保证共享数据的安全性。

 

二,CAS是什么?

    cas是compare and swap 的缩写,比较并替换,cas有三个操作数:内存地址V,旧的预期值A,即将要更新的目标值B

    cas指令执行时,当前仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否者就什么都不做,整个比较并替换的操作是一个原子操作。

    如果发现V中存的值,和预期值A不相等,那么就会提交失败,重写提交,这就叫自旋。

    在这里体会什么是乐观锁,cas就觉得,并没有多少线程会去竞争这个锁,所以不会去等待,而是不断的去尝试更新值。并且认为,满足更新条件是个大概率事件。(很乐观)

三,CAS实现原理是什么?

    点进方法看:

public final boolean compareAndSet(int expect, int update) {

        return unsafe.compareAndSwapInt(this, valueOffset, expect,  update);

    }

    这个就和我们lock里面发现的操作都是一样的,使用unsafe类的方法调用,这里在eclipse就看不见再里面的源码了,因为unsafe类中的方法都是native方法,由c,c++直接在操作系统上面操作。

继续跟进(我们借用大佬的图说明):

这里调用cmpxchg方法,它在Linux和windows实现不一样,我们分别看一下

linux_x86

windows_x86

cmpxchg到底在做什么?

    答:os::is_MP()判断这个系统是否是多处理器,如果是多处理,返回1,否者返回0.

            LOCK_IF_MP(mp) 是根据mp的值来决定是否为cmpxchg指令添加lock前缀:如果是多处理器,则需要为cmpxchg指令添加lock前缀。(这是一种优化手段,只在多核情况添加)

 

    这里的Lock前缀指令,和处理器 实现 原子操作有关系,和内存屏障有关系。

cmpxchg源码:

jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte*dest, jbyte compare_value) {
         assert (sizeof(jbyte) == 1,"assumption.");
         uintptr_t dest_addr = (uintptr_t) dest;
         uintptr_t offset = dest_addr % sizeof(jint);
         volatile jint*dest_int = ( volatile jint*)(dest_addr - offset);
         // 对象当前值
         jint cur = *dest_int;
         // 当前值cur的地址
         jbyte * cur_as_bytes = (jbyte *) ( & cur);
         // new_val地址
         jint new_val = cur;
         jbyte * new_val_as_bytes = (jbyte *) ( & new_val);
          // new_val存exchange_value,后面修改则直接从new_val中取值
         new_val_as_bytes[offset] = exchange_value;
         // 比较当前值与期望值,如果相同则更新,不同则直接返回
         while (cur_as_bytes[offset] == compare_value) {
          // 调用汇编指令cmpxchg执行CAS操作,期望值为cur,更新值为new_val
             jint res = cmpxchg(new_val, dest_int, cur);
             if (res == cur) break;
             cur = res;
             new_val = cur;
             new_val_as_bytes[offset] = exchange_value;
         }
         // 返回当前值
         return cur_as_bytes[offset];
}

四,CAS存在的问题

    1.易于理解的,循环时间长开销很大:因为cas尝试失败就会一直自旋,循环的去尝试更新。

    2.只能保证一个共享变量的原子操作:对多个共享变量操作时,循环cas就无法保证操作原子性,这个时候需要用锁,:解决办法:将多个变量合成为一个变量进行操作(类的封装)

    3.著名ABA问题:我们直到cas的操作前提是保证:地址v里面存放的值,和预期值A一样,那么就操作更新为B,但是如果, 有一个人把A的值,改编成为B,又从B改变回A,那么对于之前那个执行CAS操作的线程来说,是无法察觉的。解决办法:给每一次修改附上一个版本号,1.5推出了AtomicStampedReference来解决ABA问题,它的作用就是对四个标志,引用等等进行相等判断,全部都满足,才进行操作。

    

 

参考链接: https://blog.csdn.net/v123411739/article/details/79561458(有源码)

                  https://www.jianshu.com/p/ae25eb3cfb5d(基本使用与流程)

                 https://zhuanlan.zhihu.com/p/94762520?utm_source=wechat_timeline(有深度)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值