Java成神之路——一文搞懂CAS

CAS是什么

CAS(Compare And Swap)比较与替换。在高并发编程中大量使用。

为什么需要CAS

在多线程环境下对一个变量进行并发修改是不能保证原子性的。例如多线程下对一个int类型变量进行++操作,线程A读取int i = 0; 对 i 进行++操作,线程B在线程A修改更新 i 之前去读取 i = 0; 也进行++ 操作;期待的结果为2,但最终结果却是1。有人可能会问这不是可见行问题么?添加 volatile修饰能不能解决,答案是不能。线程B在读取 i 时,线程A还没有进行修改更新(并发执行)。

对于变量自增的并发解决方法AtomicInteger就是使用volatile+CAS来保证多线程下变量的原子性的。

CAS实现原理

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。内存位置的值V与预期值比较,匹配更新为B是底层同步的。

CAS 的缺点

  1. ABA问题,如果有一个变量A,线程1修改变量A为B,线程2又把变量B改回了A,线程3预期变量值为A才会把值改为C,线程3以为变量的值没有变化,但是它却被修改过。解决方案,添加一个版本号,每次对变量进行修改的时候对版本号+1,比较的时候看下版本号是否一致。
  2. CAS无锁自旋修改变量,如果一个变量一直修改不成功,很消耗cpu资源。

CAS在AtomicInteger中的应用

AtomicInteger 可以对变量进行线程安全的原子的增减操作。

// 底层维护保存的变量,使用volatile保证内存可见
  private volatile int value;
// 保存value在类信息中的偏移量
  private static final long valueOffset;

  static {
       try {
       // 使用unsafe类初始化 value 的偏移量
           valueOffset = unsafe.objectFieldOffset
               (AtomicInteger.class.getDeclaredField("value"));
       } catch (Exception ex) { throw new Error(ex); }
   }

getAndIncrement 方法

    public final int getAndIncrement() {
  // 还如当前对象,value偏移量,增加数量
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    public final int getAndAddInt(Object var1, long var2, int var4) {
      int var5;
      // 自旋一直尝试修改
      do {
          // 拿到当前的value值
          var5 = this.getIntVolatile(var1, var2);
      // CAS进行比较和替换
      // 参数(当前对象,value地址偏移量,预期值A,新值B)
      } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
	  // 修改成功后返回
      return var5;
    }

Unsafe类的作用于使用

参考文章
交流Q群 892480622

你可能感兴趣的:(Java成神之路)