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

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里面的实现总来说就是这样的。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券