AtomicInteger源码及CAS深度分析

1、原子类

  • 可以实现一些原子操作
  • 基于CAS
    下面就以AtomicInteger为例。

2、AtomicInteger

在没有AtomicInteger之前,对于一个Integer的线程安全操作,是需要使用同步锁来实现的,当然现在也可以通过ReentrantLock来实现,但是最好最方便的实现方式是采用AtomicInteger。

具体示例:

package com.collection.test;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 原子类的测试
 */
public class AtomicTest {
    private static AtomicInteger atomicInteger = new AtomicInteger();

    //获取当前值
    public static void getCurrentValue(){
        System.out.println(atomicInteger.get());//-->0
    }

    //设置value值
    public static void setValue(){
        atomicInteger.set(12);//直接用12覆盖旧值
        System.out.println(atomicInteger.get());//-->12
    }

    //根据方法名称getAndSet就知道先get,则最后返回的就是旧值,如果get在后,就是返回新值
    public static void getAndSet(){
        System.out.println(atomicInteger.getAndSet(15));//-->12
    }

    public static void getAndIncrement(){
        System.out.println(atomicInteger.getAndIncrement());//-->15
    }

    public static void getAndDecrement(){
        System.out.println(atomicInteger.getAndDecrement());//-->16
    }

    public static void getAndAdd(){
        System.out.println(atomicInteger.getAndAdd(10));//-->15
    }

    public static void incrementAndGet(){
        System.out.println(atomicInteger.incrementAndGet());//-->26
    }

    public static void decrementAndGet(){
        System.out.println(atomicInteger.decrementAndGet());//-->25
    }

    public static void addAndGet(){
        System.out.println(atomicInteger.addAndGet(20));//-->45
    }

    public static void main(String[] args) {
        AtomicTest test = new AtomicTest();
        test.getCurrentValue();
        test.setValue();
        //返回旧值系列
        test.getAndSet();
        test.getAndIncrement();
        test.getAndDecrement();
        test.getAndAdd();
        //返回新值系列
        test.incrementAndGet();
        test.decrementAndGet();
        test.addAndGet();

    }
}
private volatile int value;// 初始化值

    /**
     * 创建一个AtomicInteger,初始值value为initialValue
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    /**
     * 创建一个AtomicInteger,初始值value为0
     */
    public AtomicInteger() {
    }

    /**
     * 返回value
     */
    public final int get() {
        return value;
    }

    /**
     * 为value设值(基于value),而其他操作是基于旧值<--get()
     */
    public final void set(int newValue) {
        value = newValue;
    }

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

    /**
     * 基于CAS为旧值设定新值,采用无限循环,直到设置成功为止
     * 
     * @return 返回旧值
     */
    public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();// 获取当前值(旧值)
            if (compareAndSet(current, newValue))// CAS新值替代旧值
                return current;// 返回旧值
        }
    }

    /**
     * 当前值+1,采用无限循环,直到+1成功为止
     * @return the previous value 返回旧值
     */
    public final int getAndIncrement() {
        for (;;) {
            int current = get();//获取当前值
            int next = current + 1;//当前值+1
            if (compareAndSet(current, next))//基于CAS赋值
                return current;
        }
    }

    /**
     * 当前值-1,采用无限循环,直到-1成功为止 
     * @return the previous value 返回旧值
     */
    public final int getAndDecrement() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    /**
     * 当前值+delta,采用无限循环,直到+delta成功为止 
     * @return the previous value  返回旧值
     */
    public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }

    /**
     * 当前值+1, 采用无限循环,直到+1成功为止
     * @return the updated value 返回新值
     */
    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;//返回新值
        }
    }

    /**
     * 当前值-1, 采用无限循环,直到-1成功为止 
     * @return the updated value 返回新值
     */
    public final int decrementAndGet() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return next;//返回新值
        }
    }

    /**
     * 当前值+delta,采用无限循环,直到+delta成功为止  
     * @return the updated value 返回新值
     */
    public final int addAndGet(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return next;//返回新值
        }
    }

    /**
     * 获取当前值
     */
    public int intValue() {
        return get();
    }

说明:使用与源代码都简单到爆了!自己看看注释。

注意:

value是volatile的,关于volatile的相关内容见《附2 volatile》,具体链接:http://www.cnblogs.com/java-zhao/p/5125698.html
单步操作:例如set()是直接对value进行操作的,不需要CAS,因为单步操作就是原子操作。
多步操作:例如getAndSet(int newValue)是两步操作–>先获取值,在设置值,所以需要原子化,这里采用CAS实现。
对于方法是返回旧值还是新值,直接看方法是以get开头(返回旧值)还是get结尾(返回新值)就好
CAS:比较CPU内存上的值是不是当前值current,如果是就换成新值update,如果不是,说明获取值之后到设置值之前,该值已经被别人先一步设置过了,此时如果自己再设置值的话,需要在别人修改后的值的基础上去操作,否则就会覆盖别人的修改,所以这个时候会直接返回false,再进行无限循环,重新获取当前值,然后再基于CAS进行加减操作。
如果还是不懂CAS,类比数据库的乐观锁
补充一个东西:

// 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的所有属性,其中value存的是当前值,而当前值存放的内存地址可以通过valueOffset来确定。实际上是“value字段相对Java对象的起始地址的偏移量”

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

CAS方法:通过对比“valueOffset上的value”与expect是否相同,来决定是否修改value值为update值。

CAS

CAS:Compare and Swap, 翻译成比较并交换。

java.util.concurrent包中借助CAS实现了区别于synchronouse同步锁的一种乐观锁。
CAS应用

CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

非阻塞算法 (nonblocking algorithms)

一个线程的失败或者挂起不应该影响其他线程的失败或挂起的算法。

现代的CPU提供了特殊的指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet() 就用这些代替了锁定。

拿出AtomicInteger来研究在没有锁的情况下是如何做到数据正确性的。

private volatile int value;

首先毫无以为,在没有锁的机制下可能需要借助volatile原语,保证线程间的数据是可见的(共享的)。

这样才获取变量的值的时候才能直接读取。

public final int get() {
return value;
}

然后来看看++i是怎么做到的。

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

在这里采用了CAS操作,每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。

而compareAndSet利用JNI来完成CPU指令的操作。

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

整体的过程就是这样子的,利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。其它原子操作都是利用类似的特性完成的。

其中

unsafe.compareAndSwapInt(this, valueOffset, expect, update);
类似:
if (this == expect) {
this = update
return true;
} else {
return false;
}

那么问题就来了,成功过程中需要2个步骤:比较this == expect,替换this = update,compareAndSwapInt如何这两个步骤的原子性呢? 参考CAS的原理。

CAS缺点

CAS虽然很高效的解决原子操作,但是CAS仍然存在三大问题。ABA问题,循环时间长开销大和只能保证一个共享变量的原子操作

  1. ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。
    从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
    关于ABA问题参考文档: http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html
  2. 循环时间长开销大。自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。
  3. 只能保证一个共享变量的原子操作。当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。

concurrent包的实现
由于java的CAS同时具有 volatile 读和volatile写的内存语义,因此Java线程之间的通信现在有了下面四种方式:

A线程写volatile变量,随后B线程读这个volatile变量。
A线程写volatile变量,随后B线程用CAS更新这个volatile变量。
A线程用CAS更新一个volatile变量,随后B线程用CAS更新这个volatile变量。
A线程用CAS更新一个volatile变量,随后B线程读这个volatile变量。
Java的CAS会使用现代处理器上提供的高效机器级别原子指令,这些原子指令以原子方式对内存执行读-改-写操作,这是在多处理器中实现同步的关键(从本质上来说,能够支持原子性读-改-写指令的计算机器,是顺序计算图灵机的异步等价机器,因此任何现代的多处理器都会去支持某种能对内存执行原子性读-改-写操作的原子指令)。同时,volatile变量的读/写和CAS可以实现线程之间的通信。把这些特性整合在一起,就形成了整个concurrent包得以实现的基石。如果我们仔细分析concurrent包的源代码实现,会发现一个通用化的实现模式:

首先,声明共享变量为volatile;
然后,使用CAS的原子条件更新来实现线程之间的同步;
同时,配合以volatile的读/写和CAS所具有的volatile读和写的内存语义来实现线程之间的通信。
AQS,非阻塞数据结构和原子变量类(java.util.concurrent.atomic包中的类),这些concurrent包中的基础类都是使用这种模式来实现的,而concurrent包中的高层类又是依赖于这些基础类来实现的。从整体来看,concurrent包的实现示意图如下:
这里写图片描述

参考链接: http://www.cnblogs.com/java-zhao/p/5140158.html
https://blog.csdn.net/hsuxu/article/details/9467651

猜你喜欢

转载自blog.csdn.net/u012794505/article/details/81287119