美文网首页Java 并发编程&JUC源码解析
Java Concurrent Atomic实现原理&源码解读(

Java Concurrent Atomic实现原理&源码解读(

作者: 邹志全 | 来源:发表于2019-07-15 22:26 被阅读0次

    JDK 10,可以说是很新了,比起JDK 8更新了不少实现,比如说下面会讲到VarHandle

    说了这么多篇原理类的,终于要开始看源码了。这一篇描述atomic,主要简述Concurrent里面的核心使用的类,其他相关的,大家看的时候可以顺便进去看两眼内容不多,还可以。

    是时候看一眼JDK了,忽然发现IDEA可能是读源码最好用的工具了,sun包的暂时不看就单说Concurrent里面的 。

    image.png
    AtomicBoolean、AtomicInteger等是atomic中比较经典的一类,这里不描述API,就单说源码实现。

    AtomicBoolean

    首先看到的是:

    public class AtomicBoolean implements java.io.Serializable {
        private static final long serialVersionUID = 4654671469794556979L;
        private static final VarHandle VALUE;
        static {
            try {
                MethodHandles.Lookup l = MethodHandles.lookup();
                VALUE = l.findVarHandle(AtomicBoolean.class, "value", int.class);
            } catch (ReflectiveOperationException e) {
                throw new Error(e);
            }
        }
    

    不仅是AtomicBoolean,其他的原子类也都是可序列化,并且持有一个通过JNI调用本地方法的VarHandle,这个类实在sun.lang里实现的,也不复杂有兴趣可以去看看。首先完成静态属性的初始化,(这里可以顺道提一点类的初始化顺序:父类静态变量,父类静态初始化块,子类静态变量,子类静态初始化块,非静态属性与之相同。父类先于子类,静态属性是在载入类是就已经初始化了。)
    再往下看:

    private volatile int value;
    

    一个非对象类型的volatile int value值,这个值是整个AtomicBoolean逻辑的核心,后续的实现的功能函数都会围绕它展开。1:true 0:false,同时volatile避免的必要是的可见性及防止重排序。

    /**
    * Creates a new {@code AtomicBoolean} with the given initial value.
    *
    * @param initialValue the initial value
    */
    public AtomicBoolean(boolean initialValue) {
        value = initialValue ? 1 : 0;
    }
    /**
    * Creates a new {@code AtomicBoolean} with initial value {@code false}.
    */
    public AtomicBoolean() {
    }
    

    这个是构造函数,可以清晰看到value的使用。

    /**
    * Returns the current value,
    * with memory effects as specified by {@link VarHandle#getVolatile}.
    *
    * @return the current value
    */
    public final boolean get() {
        return value != 0;
    }
    

    get函数直接返回就ok,因为是volatile,volatile的直接写操作是原子的,保证了return的值绝对是各线程更新后最新的,不存在在工作内存为刷入主存,而导致的value值不一致的情况。问题不大,继续向下:

    /**
    * Atomically sets the value to {@code newValue}
    * if the current value {@code == expectedValue},
    * with memory effects as specified by {@link VarHandle#compareAndSet}.
    *
    * @param expectedValue the expected value
    * @param newValue 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(boolean expectedValue, boolean newValue) {
        return VALUE.compareAndSet(this,
                                   (expectedValue ? 1 : 0),
                                   (newValue ? 1 : 0));
    }
    

    这里开始使用JNI调用本地函数了,进到源码里看一眼:这里我就不放注释了,着实太长。(这个方法在VarHandle里)

    public final native
    @MethodHandle.PolymorphicSignature
    @HotSpotIntrinsicCandidate
    boolean compareAndSet(Object... args);
    

    可以看到修饰这个函数的关键词,native 关键词,native主要有两个作用
    1、JDK依赖于C++函数直接完成某些系统调用及函数.
    2、Java C++联合开发时,可以顺道调用C++工具函数,当然了驱动外部程序现在有了ProcessBuilder但是两者还是存在很大区别,JNI是直接调用的函数,而ProcessBuilder是完成外部完整程序的运行,比如说一条shell,一个Cpp文件,给我们直观的感受是开了个进程。
    这里native 的使用场景,完全符合第一种场景。

    Java 8及之前底层能保证比较并操作原子性的方式有这么几种:(因为在底层讨论,所以AtomicInteger属于上层实现了,这里不算是一种,但在上层应用中还是算一种原子的)

    1、使用原子性的FieldUpdaters,利用了反射机制,操作开销也会更大;
    2、使用sun.misc.Unsafe提供的JVM内置函数API,虽然这种方式比较快,但它会损害安全性和可移植性。
    在Java 9时出现了VarHandle来部分替代java.util.concurrent.atomicsun.misc.Unsafe

    VarHandle 可以与任何字段、数组元素或静态变量关联,支持在不同访问模型下对这些类型变量的访问,包括简单的 read/write 访问,volatile 类型的 read/write 访问,和 CAS(compare-and-swap)等。虽然unsafe仍旧保留使用,但Java 9之后用的大部分就都是VarHandle了,官方称为变量句柄,然后VarHandle依赖于VarForm使用,然后VarForm包含一下两个变量及相关操作以完成VarHandle的支持。

    final @Stable MethodType[] methodType_table;
    final @Stable MemberName[] memberName_table;
    

    主要支持链接所有签名的多态方法,注意compare的第一个注解。
    然后第二个注解的含义:
    如果使用@SetNativeMethodPrefix注解本地方法,run的时候就会得到一个警告。
    比如:

    For instance for Thread::isInterrupted:
    Compiler intrinsic is defined for method
    [java.lang.Thread.isInterrupted(Z)Z], but the method is not annotated with
    @HotSpotIntrinsicCandidate. Method will not be inlined.
    

    然后boolean类型原子性的保证到这里就看完了。

    AtomicInteger

    public class AtomicInteger extends Number implements java.io.Serializable {
        private static final long serialVersionUID = 6214790243416807050L;
        /*
         * This class intended to be implemented using VarHandles, but there
         * are unresolved cyclic startup dependencies.
         */
        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
        private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
        private volatile int value;
    

    跟boolean一样,支持序列化,同样维护着一个private volatile int value不同的是AtomicInteger是依赖于UnSafe调用外部方法实现的

    下面是Unsafe中compareAndSet的实现:

    /**
    * Atomically updates Java variable to {@code x} if it is currently
    * holding {@code expected}.
    *
    * <p>This operation has memory semantics of a {@code volatile} read
    * and write.  Corresponds to C11 atomic_compare_exchange_strong.
    *
    * @return {@code true} if successful
    */
    @HotSpotIntrinsicCandidate
    public final native boolean compareAndSetInt(Object o, long offset,
                                                 int expected,
                                                 int x);
    

    同样调用本地方法,不多赘述。
    其他方法的使用,不如说+1、-1等看官方API就可以了,底层的函数比较简单通俗易懂。

    Atomic*Array

    基于VarHandle实现,其中维护了一个数组对象,但是相对于其他对象来说,使用了MethodHandles.arrayElementVarHandle(int[].class)来完成初始。

    public class AtomicIntegerArray implements java.io.Serializable {
        private static final long serialVersionUID = 2862133569453604235L;
        private static final VarHandle AA
            = MethodHandles.arrayElementVarHandle(int[].class);
        private final int[] array;
    

    其中所调用的本地方法也与其他不同,volatile相关特性依赖于native函数实现。

    public final native
    @MethodHandle.PolymorphicSignature
    @HotSpotIntrinsicCandidate
    Object getVolatile(Object... args);
    

    AtomicBoolean:VarHandle
    AtomicInteger:UnSafe
    AtomicLong:UnSafe
    AtomicReference:Varhandle,但与AtomicBoolean不同的是AtomicReference多了一个泛型处理。
    Atomic里面的实现总来说就是这样的。

    相关文章

      网友评论

        本文标题:Java Concurrent Atomic实现原理&源码解读(

        本文链接:https://www.haomeiwen.com/subject/hdtkkctx.html