JDK并发包温故知新系列(四)—— CAS原理与JDK8的优化

什么是CAS

CAS-CompareAndSet,是JDK原子变量类AtomicInteger、AtomicLong、AtomicInteger、AtomicBoolean、AtomicReference等实现的基础,例如对于一个共享变量int,就算是简单的自增操做也不是原子性的,多线程同时自增,可能会致使变量的值比预期结果小。可是可使用AtomicInteger的incrementAndGet() 方法操做变量,这样结果和预期值同样。跟传统的加锁不一样,getAndDecrement()方法并无给代码加锁。代码相似于:算法

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}

底层经过sun.misc.Unsafe的本地方法compareAndSwapInt实现,这个方法是原子的。多线程

与synchronized的对比

  • 乐观锁与悲观锁的区别
  • 性能对比

synchronized是阻塞的,CAS更新是非阻塞的,只是会重试,不会有线程上下文切换开销,对于大部分比较简单的操做,不管是在低并发仍是高并发状况下,这种乐观非阻塞方式的性能都要远高于悲观阻塞式方式。并发

应用场景

  • 用来实现乐观非阻塞算法,确保当前线程方法体内使用的共享变量不被其余线程改变,CAS普遍运用在非阻塞容器中。
  • 用来实现悲观阻塞式算法,其用在了显式锁的原理实现,如可重入计数中,调用lock()方法时将经过CAS方法将其设为1,调用unlock则设为递减1。若是同时多个线程调用Lock方法那么必然会致使原子修改不成功,保证了锁的机制,排他性。

可能存在的问题

  • ABA问题,普通的CAS操做并非原子的,由于有可能另外一个线程改了值可是又改回了值,那么乐观锁的方式是不能保证原子性的,若业务须要规避这种状况那么可使用AtomicStampedReference的compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)方法,只有值和时间戳都相等的时候才进行原子更新,每次更新都把当前时间修改进原子变量。

JDK8的优化

JAVA8新增了LongAdder、DoubleAdder对原子变量进行进一步优化,主要是利用了分段CAS的机制,若是不用LongAdder,用AtomicLong的话,在高并发状况下,会产生一直自旋,致使效率不高。他将一个数分红若干个数,CompareAndSet方法的参数只是比较的这若干个数中的一个数,从而下降了自旋的几率,提升了效率。高并发

相关文章
相关标签/搜索