并发系列(四)-----CAS

一 简介

 保证Java中的原子操作方式有两种方式
  1 加锁(能够理解悲观锁机制)
  2 CAS(能够理解为乐观锁机制)
  CAS全称是Compare and Swap 即比较并替换。在JDK中许多地方均可以看到它的身影,好比AQS同步组件,Atomic原子类操做等等都是以CAS实现的。其中java.util.concurrent 中的许多概念源自 Doug Lea 的 util.concurrent 库,而Doug lea大神在同步组件中大量使用使用CAS技术鬼斧神工地实现了Java多线程的并发操做。java


 二 CAS原理

在CAS中有三个参数:内存值V、旧的预期值A,要更新的值B。更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改成B。
来看一下Atomic包的源码中是如何使用的CAS,并分析它的原理
AtomicInteger的成员变量多线程

     // setup to use Unsafe.compareAndSwapInt for updates 
     private static final Unsafe unsafe = Unsafe.getUnsafe();
     private static final long valueOffset;
     static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    private volatile int value;

  

AtomicInteger的成员变量进行说明
1 Unsafe 
Java语言不像C,C++那样能够直接访问底层操做系统,可是JVM为咱们提供了一个后门,这个后门就是unsafe。unsafe为咱们提供了硬件级别的原子操做 。
2 valueOffset
至于valueOffset对象,是经过unsafe.objectFieldOffset方法获得,所表明的是AtomicInteger对象value成员变量在内存中的偏移量。咱们能够简单地把valueOffset理解为value变量的内存地址。
3 value
被volatile所修饰被volatile的特色就不在说明了上一篇文章已经提到过了。
AtomicInteger的compareAndSet()并发

  /**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    //Unsafe.class
    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

 

这个方法是修改值的方法,参数要求传两个参数指望值和要更新的值,若是更新成功的话返回true,反之亦然。在方法内部调用的是Unsafe的compareAndSwapInt,传而unsafe的compareAndSwapInt方法参数包括了这三个基本元素:valueOffset参数表明了V,expect参数表明了A,update参数表明了B。app

正是unsafe的compareAndSwapInt方法保证了Compare和Swap操做之间的原子性操做。this


三 CAS的问题


 1.ABA问题
由于CAS须要在操做值的时候,检查值有没有发生变化,若是没有发生变化则更新,可是若是一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,可是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A→B→A就会变成1A→2B→3A。从Java 1.5开始,JDK的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类compareAndSet方法的做用是首先检查当前引用是否等于预期引用,而且检查当前标志是否等于预期标志,若是所有相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
2.循环时间长CPU开销大
自旋CAS若是长时间不成功,会给CPU带来很是大的执行开销
3.只能保证一个共享变量的原子操作spa

相关文章
相关标签/搜索