我的jdk源码(二十四):AtomicInteger类和CAS机制

一、概述

    AtomicInteger类处于java.util.concurrent.atomic包下,与其他原子操作的类一样,底层都是采用CAS机制,调用了Unsafe类的CAS方法实现的。我们先分析一下AtomicInteger类的源码,再来分析CAS机制的种种。

二、源码分析

    1. 类的声明

public class AtomicInteger extends Number implements java.io.Serializable

    继承了 Number, 这主要是提供方法将数值转化为 byte, double 等方便 Java 开发者使用; 实现了 Serializable, 为了网络传输等的序列化用, 编码时最好手动生成序列化 ID, 让 javac 编译器生成开销大, 而且可能造成意想不到的状况。

    2. 成员变量

    //序列化标识id
    private static final long serialVersionUID = 6214790243416807050L;

    //Unsafe类的一个实例,提供一些不安全的操作的方法用于直接操作内存,一般不会在自己的程序中使用该类
    //在这里主要用到其中的objectFieldOffset、putOrderedInt、compareAndSwapInt方法
    private static final Unsafe unsafe = Unsafe.getUnsafe();

    //value成员属性的内存地址相对于对象内存地址的偏移量
    private static final long valueOffset;

    ////初始化valueOffset,通过unsafe.objectFieldOffset方法获取成员属性value内存地址相对于对象内存地址的偏移量
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    //int的值,volatile修饰,保证线程之间的可见性
    private volatile int value;

     Unsafe类是一个JDK内部使用的专属类,我们自己的应用程序无法直接使用Unsafe类。通过观察源码,可以知道获得Unsafe实例的方法是调动其工厂方法getUnsafe(),源码如下:

    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class var0 = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
            throw new SecurityException("Unsafe");
        } else {
            return theUnsafe;
        }
    }

    它会检查调用getUnsafe()函数的类,判断调用这的类加载器是否是系统类加载器(系统类加载器为null),如果不是就直接抛出异常,拒绝工作,这也是为什么我们写的程序没法直接使用这个类的原因。

    3. 构造函数

    //int参数类型的构造函数
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    //无参构造函数,初始值是0
    public AtomicInteger() {
    }

    4. 其他方法

    /**
     * 获取int值
     */
    public final int get() {
        return value;
    }

    /**
     * 设为指定值
     */
    public final void set(int newValue) {
        value = newValue;
    }

    /**
     * 最终设为指定值,但其它线程不能马上看到变化,会延时一会
     */
    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

    /**
     * 以原子方式设置为给定值,并返回旧值
     */
    public final int getAndSet(int newValue) {
        //乐观锁,非阻塞同步方式,循环调用compareAndSet,也就是自旋,直到成功
        for (;;) {
            int current = get();
            //CAS操作,期待值current与内存中的值比较,相等的话设为newValue值
            //否则下个循环,调用get()获取current值,继续执行CAS操作直到成功
            if (compareAndSet(current, newValue))
                return current;
        }
    }

    /**
     * CAS操作,现代CPU已广泛支持,是一种原子操作;
     * 简单地说,当期待值expect与valueOffset地址处的值相等时,设置为update值
     */
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    /**
     * 弱比较
     */
    public final boolean weakCompareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    //调用unsafe.getAndAddInt方法进行数值操作
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }

    //调用unsafe.getAndAddInt方法进行数值操作
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }

    //调用unsafe.getAndAddInt方法进行数值操作
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

    //调用unsafe.getAndAddInt方法进行数值操作
    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
    }

    //调用unsafe.getAndAddInt方法进行数值操作
    public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
    }

    /**
     * 原子操作,自增,返回旧值
     */
    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    /**
     * 原子操作,自减,返回旧值
     */
    public final int getAndDecrement() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    /**
     * 原子操作,加上一个数,返回旧值
     */
    public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }

    /**
     * 原子操作,自增,返回新值
     */
    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    /**
     * 原子操作,自减,返回新值
     */
    public final int decrementAndGet() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    /**
     * 原子操作,加上一个数,返回新值
     */
    public final int addAndGet(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return next;
        }
    }

    /**
     * Returns the String representation of the current value.
     * @return the String representation of the current value.
     */
    public String toString() {
        return Integer.toString(get());
    }


    public int intValue() {
        return get();
    }

    public long longValue() {
        return (long)get();
    }

    public float floatValue() {
        return (float)get();
    }

    public double doubleValue() {
        return (double)get();
    }

}

    5. getAndAddInt()方法

public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
 
        return var5;
    }

    第一个参数var1为给定的对象,var2为对象内的偏移量(其实就是一个字段到对象头部的偏移量,通过这个偏移量可以快速定位字段),var4表示期望值,var5表示要设置的值。如果指定的字段的值等于var4,那么就会把它设置为var5。

    6. CAS机制原理

    CAS全拼又叫做compareAndSwap,从名字上的意思就知道是比较交换的意思。它包含 3 个参数 CAS(V,E,N),V表示要更新变量的值,E表示预期值,N表示新值。仅当 V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做两个更新,则当前线程则什么都不做。最后,CAS 返回当前V的真实值。

    7. CAS的优势

    CAS是一种乐观锁,而且是一种非阻塞的轻量级的乐观锁。当一个线程想要获得锁,对方会给一个回应表示这个锁能不能获得。在资源竞争不激烈的情况下性能高,相比synchronized重量锁,synchronized会进行比较复杂的加锁,解锁和唤醒操作。

    8. CAS存在的问题

    (1) ABA问题

    * 问题描述:CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。

    * 解决办法:

        a. 用CAS的一个变种DCAS,DCAS是对于每一个V增加一个引用的表示修改次数的标记符。对于每个V,如果引用修改了一次,这个计数器就加1,然后再这个变量需要更新的时候,就同时检查变量的值和计数器的值。都符合条件才可以修改V值。

        b. 通过将标记与要进行CAS操作的每个值相关联,并原子地更新值和标记。也就是一旦V第一次被使用,就不会再重复使用,如有需要则分配新的V。垃圾收集器可以检查V,保证其不被循环使用,直到当前的访问操作全部结束。使用 AtomicStampedReference 来解决CAS中的ABA问题,它不再像compareAndSet方法 中只比较内存中的值也当前值是否相等,而且先比较引用是否相等,然后比较值是否相等。

        c. 设置一个版本号,每次修改都会修改版本号,每次对比的时候不仅对比值,还要对比版本号来保证数据没被修改。

    (2) 自旋时间长导致开销大

    * 问题描述:自旋 CAS 如果长时间不成功,会给 CPU 带来非常大的执行开销。

    * 解决办法:如果JVM能支持处理器提供的 pause 指令那么效率会有一定的提升,pause 指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使 CPU 不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation:内存顺序冲突一般是由伪/假共享引起,假共享是指多个 CPU 同时修改同一个缓存行的不同部分而引起其中一个CPU的操作无效,当出现这个内存顺序冲突时,CPU必须清空流水线)而引起 CPU 流水线被清空(CPU pipeline flush),从而提高 CPU 的执行效率。

三、总结

    通过本篇博文,对原子包java.util.concurrent.atomic下的AtomicInteger类进行了分析,其他的类也比较类似,都是底层采用了CAS机制完成各种操作。也学习了CAS的原理和存在的问题以及解决办法。

    更多精彩内容,敬请扫描下方二维码,关注我的微信公众号【Java觉浅】,获取第一时间更新哦!

猜你喜欢

转载自blog.csdn.net/qq_34942272/article/details/106821113