java 原子类的一些技术要点

引用了其他一些博文的片段

1.jdk8 中的原子类都是用无锁方式和volatile修饰变量实现的(乐观锁来实现,具体是cas,比较并交换,该指令有cpu保证原子性),原子类在java.util.concurrent.atomic包中,包括原子更新基本类型(AtomicBoolean,AtomicInteger,AtomicLong),原子更新数组,通过原子的方式更新数组里的某个元素(AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray),原子更新引用类型(AtomicReference,AtomicReferenceFieldUpdater,AtomicMarkableReferce),原子更新字段类(AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicStampedFieldUpdater,AtomicReferenceFieldUpdater)

 

2.java原子类使用的cas函数,是在unsafe类中,Unsafe类所有方法都是native的,直接调用操作系统底层资源执行相应任务,它可以像C一样操作内存指针,是非线程安全的。Java方法无法直接访问地层系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存数据。其内部方法操作可以像C的指针一样直接操作内存。

 

3.操作系统如何保证指令的原子性呢,

1) 关中断,cpu一条执行不被中断

2)总线锁,当一个处理器想要更新某个变量的值时,向总线发出LOCK#信号,此时其他处理器的对该变量的操作请求将被阻塞,发出锁定信号的处理器将独占共享内存,于是更新就是原子性的了。

 

4.AtomicInteger

会获取value对象的地址,存入到valueOffset的变量中。

    // 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;

通过unsafe类的getAndAddInt来实现原子赠一

    /**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

通过 unsafe类的compareAndSwapInt方法,实现对integer的比较和交换

    /**
     * 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);
    }

cas失败的话,就自旋,不断重试,直到成功。

//Unsafe类中的getAndAddInt方法

public final int getAndAddInt(Object o, long offset, int delta) {        

    int v;        

    do {            

        v = getIntVolatile(o, offset);        

    } while (!compareAndSwapInt(o, offset, v, v + delta));        

    return v;

}

 

5.cas是什么?

CAS全称呼Compare-And-Swap,它是一条CPU并发原语

他的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。

CAS并发原语体现在JAVA语言中就是sun.misc.Unsafe类中各个方法。调用Unsafe类中的CAS方法,JVM会帮我们实现CAS汇编指令。这是一种完全依赖于硬件的功能,通过他实现了原子操作。由于CAS是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成数据不一致问题。

6. cas 的缺点

但是CAS机制通常也存在以下缺点:

(1)ABA问题

       如果V的初始值是A,在准备赋值的时候检查到它仍然是A,那么能说它没有改变过吗?也许V经历了这样一个过程:它先变成了B,又变成了A,使用CAS检查时

以为它没变,其实却已经改变过了。

(2)CPU开销较大

     在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。

(3)不能保证代码块的原子性

     CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。

 

7.多个变量的修改,考虑AtomicReference,多个变量修改且避免ABA的问题,考虑用AtomicStampedReference类

从AtomicReference实现类来看,AtomicReference类只是对引用的本身值的赋值做了原子保护,引用指向的对象的内容的变化,并没有提供原子保护。只是用AtomicReference来设置对象时,是新建了一个对象来设置,比较用的excpect值是cas函数之前读取出来用做比较的。如果其他线程也新建对象并设置成功的话,当前线程cas就会失败,然后不断自旋,重新读取新的对象,改变后再设置,直到成功。

参考:https://www.jianshu.com/p/882d0e2c3ea6

public class AtomicReference<V> implements java.io.Serializable {
    private static final long serialVersionUID = -1848883965231344442L;

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicReference.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile V value;

 

AtomicStampedReference类通过引入stamp,将stamp和要更新的对象reference组合成pair, 如果stamp和reference都和前期取出来的一致,才更新为新的pair。

​//关键代码

public class AtomicStampedReference<V> {

    private static class Pair<T> {

        final T reference;  //维护对象引用

        final int stamp;  //用于标志版本

        private Pair(T reference, int stamp) {

            this.reference = reference;

            this.stamp = stamp;

        }

        static <T> Pair<T> of(T reference, int stamp) {

            return new Pair<T>(reference, stamp);

        }

    }

    private volatile Pair<V> pair;

    ....

    /**

      * expectedReference :更新之前的原始值

      * newReference : 将要更新的新值

      * expectedStamp : 期待更新的标志版本

      * newStamp : 将要更新的标志版本

      */

    public boolean compareAndSet(V   expectedReference,

                                 V   newReference,

                                 int expectedStamp,

                                 int newStamp) {

        Pair<V> current = pair; //获取当前pair

        return

            expectedReference == current.reference && //原始值等于当前pair的值引用,说明值未变化

            expectedStamp == current.stamp && // 原始标记版本等于当前pair的标记版本,说明标记未变化

            ((newReference == current.reference &&

              newStamp == current.stamp) || // 将要更新的值和标记都没有变化

             casPair(current, Pair.of(newReference, newStamp))); // cas 更新pair

    }

}    

其实ABA的问题,update的值如果一直是递增的,就不会有ABA的问题,ABA问题的核心是update值变成另外的值又变回来,所以造成ABA,如果update值一直递增或者递减,就不会有ABA的问题,比如update值只能比当前值增加,就不会有ABA的问题。

8.AtomicLongArray 类,

实际也是通过要修改的数组元素的Index ,计算出相对于起始地址的偏移量,通过base+offset得到要修改的地址,通过cas来原子的修改

public class AtomicLongArray implements java.io.Serializable {
    private static final long serialVersionUID = -2308431214976778248L;

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final int base = unsafe.arrayBaseOffset(long[].class);
    private static final int shift;
    private final long[] array;

    static {
        int scale = unsafe.arrayIndexScale(long[].class);
        if ((scale & (scale - 1)) != 0)
            throw new Error("data type scale not a power of two");
        shift = 31 - Integer.numberOfLeadingZeros(scale);
    }

    private long checkedByteOffset(int i) {
        if (i < 0 || i >= array.length)
            throw new IndexOutOfBoundsException("index " + i);

        return byteOffset(i);
    }

    private static long byteOffset(int i) {
        return ((long) i << shift) + base;
    }

    /**
     * Creates a new AtomicLongArray of the given length, with all
     * elements initially zero.
     *
     * @param length the length of the array
     */
    public AtomicLongArray(int length) {
        array = new long[length];
    }

    public final long getAndSet(int i, long newValue) {
        return unsafe.getAndSetLong(array, checkedByteOffset(i), newValue);
    }

9.AtomicIntegerFieldUpdater用于原子的修改某个对象的某个Integer字段, AtomicIntegerFieldUpdater 是一个抽象类,要通过newUpdater静态函数来创建一个对象,newUpdater需要指定要原子修改的field的名称,更新类的字段必须使用public volatile修饰。AtomicIntegerFieldUpdater根据反射,通过field名字拿到该field对应Field对象,通过Field对象拿到field的地址(this.offset = U.objectFieldOffset(field); U 是unsafe对象),原子的修改field的值。

public abstract class AtomicIntegerFieldUpdater<T> {
    /**
     * Creates and returns an updater for objects with the given field.
     * The Class argument is needed to check that reflective types and
     * generic types match.
     *
     * @param tclass the class of the objects holding the field
     * @param fieldName the name of the field to be updated
     * @param <U> the type of instances of tclass
     * @return the updater
     * @throws IllegalArgumentException if the field is not a
     * volatile integer type
     * @throws RuntimeException with a nested reflection-based
     * exception if the class does not hold field or is the wrong type,
     * or the field is inaccessible to the caller according to Java language
     * access control
     */
    @CallerSensitive
    public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
                                                              String fieldName) {
        return new AtomicIntegerFieldUpdaterImpl<U>
            (tclass, fieldName, Reflection.getCallerClass());
    }

    /**
     * Protected do-nothing constructor for use by subclasses.
     */
    protected AtomicIntegerFieldUpdater() {
    }

    private static final class AtomicIntegerFieldUpdaterImpl<T>
        extends AtomicIntegerFieldUpdater<T> {
        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
        private final long offset;
        /**
         * if field is protected, the subclass constructing updater, else
         * the same as tclass
         */
        private final Class<?> cclass;
        /** class holding the field */
        private final Class<T> tclass;

        AtomicIntegerFieldUpdaterImpl(final Class<T> tclass,
                                      final String fieldName,
                                      final Class<?> caller) {
            final Field field;
            final int modifiers;
            try {
                field = AccessController.doPrivileged(
                    new PrivilegedExceptionAction<Field>() {
                        public Field run() throws NoSuchFieldException {
                            return tclass.getDeclaredField(fieldName);
                        }
                    });
                modifiers = field.getModifiers();
                sun.reflect.misc.ReflectUtil.ensureMemberAccess(
                    caller, tclass, null, modifiers);
                ClassLoader cl = tclass.getClassLoader();
                ClassLoader ccl = caller.getClassLoader();
                if ((ccl != null) && (ccl != cl) &&
                    ((cl == null) || !isAncestor(cl, ccl))) {
                    sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
                }
            } catch (PrivilegedActionException pae) {
                throw new RuntimeException(pae.getException());
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }

            if (field.getType() != int.class)
                throw new IllegalArgumentException("Must be integer type");

            if (!Modifier.isVolatile(modifiers))
                throw new IllegalArgumentException("Must be volatile type");

            // Access to protected field members is restricted to receivers only
            // of the accessing class, or one of its subclasses, and the
            // accessing class must in turn be a subclass (or package sibling)
            // of the protected member's defining class.
            // If the updater refers to a protected field of a declaring class
            // outside the current package, the receiver argument will be
            // narrowed to the type of the accessing class.
            this.cclass = (Modifier.isProtected(modifiers) &&
                           tclass.isAssignableFrom(caller) &&
                           !isSamePackage(tclass, caller))
                          ? caller : tclass;
            this.tclass = tclass;
            this.offset = U.objectFieldOffset(field);
        }

10.AtomicReferenceFieldUpdater和AtomicIntegerFieldUpdater类似,原子的修改某个对象的引用类型的成员变量的值。

11.AtomicReferenceFieldUpdater 和 AtomicReference的区别在于,AtomicReferenceFieldUpdater通常用于对于同一个对象中某个字段做原子更新,只有一个对象。AtomicReference是原子的更新引用(对象),会有多个对象。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值