java如何实现原子操做CAS

在Java中能够经过锁和循环CAS的方式来实现原子操做。java

使用循环CAS实现原子操做

JVM中的CAS操做正是利用了处理器提供的CMPXCHG指令实现的。自旋CAS实现的基本思路就是循环进行CAS操做直到成功为止。编程

CAS实现原子操做的三大问题

ABA问题,循环时间长开销大,以及只能保证一个共享变量的原子操做。数组

ABA问题

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

public boolean compareAndSet(
         V expectedReference, // 预期引用 
         V newReference, // 更新后的引用 
        int expectedStamp, // 预期标志 
        int newStamp // 更新后的标志 )

循环时间长开销大

自旋CAS若是长时间不成功,会给CPU带来很是大的执行开销。如 果JVM能支持处理器提供的pause指令,那么效率会有必定的提高。pause指令有两个做用:第 一,它能够延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间 取决于具体实现的版本,在一些处理器上延迟时间是零;第二,它能够避免在退出循环的时候 因内存顺序冲突(Memory Order Violation)而引发CPU流水线被清空(CPU Pipeline Flush),从而 提升CPU的执行效率。this

只能保证一个共享变量的原子操做

当对一个共享变量执行操做时,咱们可使用循 环CAS的方式来保证原子操做,可是对多个共享变量操做时,循环CAS就没法保证操做的原子 性,这个时候就能够用锁。还有一个取巧的办法,就是把多个共享变量合并成一个共享变量来 操做。好比,有两个共享变量i=2,j=a,合并一下ij=2a,而后用CAS来操做ij。从Java 1.5开始, JDK提供了AtomicReference类来保证引用对象之间的原子性,就能够把多个变量放在一个对 象里来进行CAS操做。线程

使用锁机制实现原子操做

锁机制保证了只有得到锁的线程才可以操做锁定的内存区域。JVM内部实现了不少种锁 机制,有偏向锁、轻量级锁和互斥锁。有意思的是除了偏向锁,JVM实现锁的方式都用了循环 CAS,即当一个线程想进入同步块的时候使用循环CAS的方式来获取锁,当它退出同步块的时 候使用循环CAS释放锁。code

原子操做类

原子更新基本类型

  • AtomicBoolean:原子更新布尔类
  • AtomicInteger:原子更新整型。
  • AtomicLong:原子更新长整型。

getAndIncrement是如何实现原子操做的呢?对象

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

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

源码中for循环体的第一步先取得AtomicInteger里存储的数值,第二步对AtomicInteger的当 前数值进行加1操做,关键的第三步调用compareAndSet方法来进行原子更新操做,该方法先检 查当前数值是否等于current,等于意味着AtomicInteger的值没有被其余线程修改过,则将 AtomicInteger的当前数值更新成next的值,若是不等compareAndSet方法会返回false,程序会进 入for循环从新进行compareAndSet操做。ip

原子更新数组

  • AtomicIntegerArray:原子更新整型数组里的元素。
  • AtomicLongArray:原子更新长整型数组里的元素。
  • AtomicReferenceArray:原子更新引用类型数组里的元素。
  • AtomicIntegerArray类主要是提供原子的方式更新数组里的整型

须要注意的是,数组value经过构造方法传递进去,而后AtomicIntegerArray会将当前数组 复制一份,因此当AtomicIntegerArray对内部的数组元素进行修改时,不会影响传入的数组。内存

原子更新引用类型

  • AtomicReference:原子更新引用类型。
  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
  • AtomicMarkableReference:原子更新带有标记位的引用类型。

原子更新字段类

  • AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
  • AtomicLongFieldUpdater:原子更新长整型字段的更新器。
  • AtomicStampedReference:原子更新带有版本号的引用类型。

参考

《java并发编程的艺术》

相关文章
相关标签/搜索