`
BrokenDreams
  • 浏览: 253983 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
68ec41aa-0ce6-3f83-961b-5aa541d59e48
Java并发包源码解析
浏览量:100150
社区版块
存档分类
最新评论

Jdk1.6 JUC源码解析(2)-atomic-AtomicXXXArray

阅读更多

Jdk1.6 JUC源码解析(2)-atomic-AtomicXXXArray

作者:大飞

 

功能简介:
  • 数组原子量。

 

源码分析:
  • 和原子量一样,数组原子量内部有一个Unsafe的静态引用。

 

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

 

 

 
  • 首先先看下AtomicIntegerArray。
       AtomicIntegerArray的构造方法如下:
    /**
     * Creates a new AtomicIntegerArray of given length.
     *
     * @param length the length of the array
     */
    public AtomicIntegerArray(int length) {
        array = new int[length];
        // must perform at least one volatile write to conform to JMM
        if (length > 0)
            unsafe.putIntVolatile(array, rawIndex(0), 0);
    }
    /**
     * Creates a new AtomicIntegerArray with the same length as, and
     * all elements copied from, the given array.
     *
     * @param array the array to copy elements from
     * @throws NullPointerException if array is null
     */
    public AtomicIntegerArray(int[] array) {
        if (array == null)
            throw new NullPointerException();
        int length = array.length;
        this.array = new int[length];
        if (length > 0) {
            int last = length-1;
            for (int i = 0; i < last; ++i)
                this.array[i] = array[i];
            // Do the last write as volatile
            unsafe.putIntVolatile(this.array, rawIndex(last), array[last]);
        }
    }
       注:当前源码来之jdk1.6,在里面会看到第一个构造方法最后就会添加一个volatile write,但在jdk1.8(从jdk1.7某个小版本开始)里面就看不到这个volatile write了,与群友讨论得知这应该是一个遗留代码,因为final完全可以保证这个语义(其他线程可以看到完全构造的内部array);第二个构造方法首先对内部final修饰的array进行赋值,然后进行数组元素copy,为了保证其他线程可以看到完全构造(内部元素copy完成)的array,所以要在copy最后加一个volatile write。
 
       相对于AtomicInteger来说,AtomicIntegerArray里面的方法都带有下标:
    /**
     * Atomically increments by one the element at index {@code i}.
     *
     * @param i the index
     * @return the updated value
     */
    public final int incrementAndGet(int i) {
        while (true) {
            int current = get(i);
            int next = current + 1;
            if (compareAndSet(i, current, next))
                return next;
        }
    }
        接下来看一下AtomicIntegerArray根据下标取值的get方法:
    /**
     * Gets the current value at position {@code i}.
     *
     * @param i the index
     * @return the current value
     */
    public final int get(int i) {
        return unsafe.getIntVolatile(array, rawIndex(i));
    }
       方法里调用了unsafe的getIntVolatile方法。在hotspot/src/share/vm/classfile/vmSymbols.hpp中找到:
  do_intrinsic(_getIntVolatile,           sun_misc_Unsafe,        getIntVolatile_name, getInt_signature,         F_RN)  \
        接着在hotspot/src/share/vm/opto/library_call.cpp中找到实现:
  case vmIntrinsics::_getIntVolatile:
    return inline_unsafe_access(!is_native_ptr, !is_store, T_INT, true);
  ...
bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, BasicType type, bool is_volatile) {
  if (callee()->is_static())  return false;  // caller must have the capability!
...//省略不重要部分
  if (is_volatile) {
    if (!is_store)
      insert_mem_bar(Op_MemBarAcquire);
    else
      insert_mem_bar(Op_MemBarVolatile);
  }
  if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder);
  return true;
}
       在方法的最后可以看到,如果是volatile且不是写操作的话,会加入一个Op_MemBarAcquire的内存屏障,再看下hotspot/src/cpu/x86/vm/x86_64.ad:
instruct membar_acquire()
%{
  match(MemBarAcquire);
  ins_cost(0);
  size(0);
  format %{ "MEMBAR-acquire ! (empty encoding)" %}
  ins_encode();
  ins_pipe(empty);
%}
       可见在x86_64下也相当于是对一个普通域的读取。
 
       最后看一下AtomicIntegerArray根据下标设置值的set方法:
    /**
     * Sets the element at position {@code i} to the given value.
     *
     * @param i the index
     * @param newValue the new value
     */
    public final void set(int i, int newValue) {
        unsafe.putIntVolatile(array, rawIndex(i), newValue);
    }
       方法里调用了unsafe的putIntVolatile方法。在hotspot/src/share/vm/classfile/vmSymbols.hpp中找到:
  do_intrinsic(_putIntVolatile,           sun_misc_Unsafe,        putIntVolatile_name, putInt_signature,         F_RN)  \
       接着在hotspot/src/share/vm/opto/library_call.cpp中找到实现:
  case vmIntrinsics::_putIntVolatile:
    return inline_unsafe_access(!is_native_ptr, is_store, T_INT, true);
       和上面getIntVolatile的实现一样:
  if (is_volatile) {
    if (!is_store)
      insert_mem_bar(Op_MemBarAcquire);
    else
      insert_mem_bar(Op_MemBarVolatile);
  }
       可见,如果是写操作做的话,加入Op_MemBarVolatile内存屏障,继续看hotspot/src/cpu/x86/vm/x86_64.ad:
instruct membar_volatile(rFlagsReg cr) %{
  match(MemBarVolatile);
  effect(KILL cr);
  ins_cost(400);
  format %{
    $$template
    if (os::is_MP()) {
      $$emit$$"lock addl [rsp + #0], 0\t! membar_volatile"
    } else {
      $$emit$$"MEMBAR-volatile ! (empty encoding)"
    }
  %}
  ins_encode %{
    __ membar(Assembler::StoreLoad);
  %}
  ins_pipe(pipe_slow);
%}
       如果是多核CPU,就会加入lock addl... 这个指令。其实就相当于是对一个volatile修饰的域的写操作喽。
 
 
  • 再看下AtomicLongArray。
       AtomicLongArray的内部结构和AtomicIntegerArray类似,这里不做分析,只看一下get和set方法中内部调用的unsafe的getLongVolatile和putLongVolatile方法。
       首先看下get方法中的getLongVolatile方法:
    /**
     * Gets the current value at position {@code i}.
     *
     * @param i the index
     * @return the current value
     */
    public final long get(int i) {
        return unsafe.getLongVolatile(array, rawIndex(i));
    }
       看下内联实现,vmSymbols.hpp,里面有如下代码:
  do_intrinsic(_getLongVolatile,          sun_misc_Unsafe,        getLongVolatile_name, getLong_signature,       F_RN)  \
       然后找到library_call.cpp中找到相应实现:
  case vmIntrinsics::_getLongVolatile:
    return inline_unsafe_access(!is_native_ptr, !is_store, T_LONG, true);
  ...
bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, BasicType type, bool is_volatile) {
  if (callee()->is_static())  return false;  // caller must have the capability!
...
  if (!is_store) {
    Node* p = make_load(control(), adr, value_type, type, adr_type, is_volatile);
...
  if (is_volatile) {
    if (!is_store)
      insert_mem_bar(Op_MemBarAcquire);
    else
      insert_mem_bar(Op_MemBarVolatile);
  }
  if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder);
  return true;
}
       首先,后面那个MemBarAcquire之前看过,在x86-64下没什么用,看下前面的make_load,找到hotspot/src/share/vm/opto/graphKit.cpp:
Node* GraphKit::make_load(Node* ctl, Node* adr, const Type* t, BasicType bt,
                          int adr_idx,
                          bool require_atomic_access) {
  assert(adr_idx != Compile::AliasIdxTop, "use other make_load factory" );
  const TypePtr* adr_type = NULL; // debug-mode-only argument
  debug_only(adr_type = C->get_adr_type(adr_idx));
  Node* mem = memory(adr_idx);
  Node* ld;
  if (require_atomic_access && bt == T_LONG) {
    ld = LoadLNode::make_atomic(C, ctl, mem, adr, adr_type, t);
  } else {
    ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt);
  }
  return _gvn.transform(ld);
}
       可见,至少对这个long值得加载是原子的(这里的原子操作应该指的是将long的高4字节和低4字节的操作合并成一个原子操作,比如某些平台不支持非volatile的long/double域的原子操作)。
 
       然后看下set方法中的setLongVolatile方法:
    /**
     * Sets the element at position {@code i} to the given value.
     *
     * @param i the index
     * @param newValue the new value
     */
    public final void set(int i, long newValue) {
        unsafe.putLongVolatile(array, rawIndex(i), newValue);
    }
       直接到library_call.cpp中看实现:
  case vmIntrinsics::_putLongVolatile:
    return inline_unsafe_access(!is_native_ptr, is_store, T_LONG, true);
  ...
bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, BasicType type, bool is_volatile) {
  if (callee()->is_static())  return false;  // caller must have the capability!
...
  if (is_volatile) {
    if (!is_store)
      insert_mem_bar(Op_MemBarAcquire);
    else
      insert_mem_bar(Op_MemBarVolatile);
  }
  if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder);
  return true;
}
       之前说过,这里加入了一个lock addl ... 的内存屏障。
 
 
 
  • 最后看下AtomicReferenceArray。
      AtomicReferenceArray的内部结构和AtomicIntegerArray类似,有一点点细节上的区别:
public class AtomicReferenceArray<E> implements java.io.Serializable {
    private static final long serialVersionUID = -6209656149925076980L;
    private static final Unsafe unsafe;
    private static final int base;
    private static final int shift;
    private static final long arrayFieldOffset;
    private final Object[] array; // must have exact type Object[]
    static {
        int scale;
        try {
            unsafe = Unsafe.getUnsafe();
            arrayFieldOffset = unsafe.objectFieldOffset
                (AtomicReferenceArray.class.getDeclaredField("array"));
            base = unsafe.arrayBaseOffset(Object[].class);
            scale = unsafe.arrayIndexScale(Object[].class);
        } catch (Exception e) {
            throw new Error(e);
        }
        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;
    }
       首先,AtomicReferenceArray里面多了一个arrayFieldOffset,这个域用来支持反序列化的。其次,与AtomicIntegerArray不同,AtomicReferenceArray并没有scale域,取而代之的是shift域。阅读代码可知,其实目的都是计算rawIndex = base + index * scale,只不过AtomicReferenceArray里面把一部分运算转换为等价的位操作(当然前提是scala为2的幂)。(了解更多位操作技巧,可参考Hacker's Delight)。
       其他涉及到unsafe的操作前面都分析过,这里不做分析了。
 
 
       好了,源码就分析到这里!
 
分享到:
评论

相关推荐

    aspose-words-15.8.0-jdk1.6

    aspose-words-15.8.0-jdk1.6aspose-words-15.8.0-jdk1.6aspose-words-15.8.0-jdk1.6aspose-words-15.8.0-jdk1.6aspose-words-15.8.0-jdk1.6aspose-words-15.8.0-jdk1.6aspose-words-15.8.0-jdk1.6aspose-words-...

    jdk-1.6-windows-64-01

    2部分: jdk-1.6-windows-64-01 jdk-1.6-windows-64-02

    okhttp3.8源码使用jdk1.6重新编译_okhttp3.8.0-jdk1.6.zip

    1.okhttp3.8源码使用jdk1.6重新编译,已集成了okio,在javaweb项目中使用,未在安卓项目中使用 2.okhttp3.8源码使用jdk1.6重新编译_okhttp3.8.0-jdk1.6.jar

    JDK 1.6 64位(jdk-6u45-windows-x64(1.6 64))

    下载的压缩包文件"jdk-6u45-windows-x64(1.6 64).exe"是Windows 64位系统的安装程序。安装过程中,用户需要选择安装路径,并设置环境变量,包括`JAVA_HOME`指向JDK的安装目录,`PATH`添加JDK的bin目录,确保系统可以...

    jdk1.6集成jjwt的问题

    2. **语法不兼容**:JJWT可能使用了Java 7或8的新语法,如Lambda表达式或方法引用,这些在JDK 1.6中不可用。 - **解决方案**:查找并替换使用了新语法的代码,或者使用不依赖这些特性的旧版JJWT。 3. **运行时...

    jdk-1.6-linux-64-3

    三部分: jdk-1.6-linux-64-1 jdk-1.6-linux-64-2 jdk-1.6-linux-64-3

    java-jdk1.6-jdk-6u45-windows-x64.zip

    1. 解压缩"java-jdk1.6-jdk-6u45-windows-x64.zip"文件,这将释放出"jdk-6u45-windows-x64.exe"可执行文件。 2. 双击运行"jdk-6u45-windows-x64.exe",安装向导会引导你完成安装过程。通常,你需要选择安装路径,...

    jdk-jdk1.6.0.24-windows-i586.exe

    标题中的"jdk-jdk1.6.0.24-windows-i586.exe"是一个Java Development Kit(JDK)的安装程序,适用于Windows操作系统且为32位版本。JDK是Oracle公司提供的一个用于开发和运行Java应用程序的软件包。这个特定的版本,...

    jdk-1.6-linux-64-2

    三部分: jdk-1.6-linux-64-1 jdk-1.6-linux-64-2 jdk-1.6-linux-64-3

    logback-cfca-jdk1.6-3.1.0.0.jar

    logback-cfca-jdk1.6-3.1.0.0.jar

    jdk-1.6-windows-32-3

    三部分: jdk-1.6-windows-32-1 jdk-1.6-windows-32-2 jdk-1.6-windows-32-3

    jdk-1.6-linux-64-1

    三部分: jdk-1.6-linux-64-1 jdk-1.6-linux-64-2 jdk-1.6-linux-64-3

    JDK1.6安装及与JDK-1.5版本共存

    ### JDK1.6安装及与JDK-1.5版本共存 #### 一、前言 随着软件开发环境的变化和技术的进步,不同的项目可能需要不同的Java版本来支持其运行。例如,在某些特定环境下,可能既需要使用JDK1.5(Java Development Kit ...

    苹果电脑安装jdk1.6 mac for jdk1.6 jdk6 安装版

    mac for jdk1.6 jdk6 安装版 里面有两个jdk1.6的安装包,都可以用 如果电脑上安装有1.7,1.8等高版本jdk就不要再下安装包了,安装包安装会报错 命令是这个:brew install java6或 brew install homebrew/cask-...

    jdk-1.6-linux-32-2

    jdk-1.6-linux-32-1 jdk-1.6-linux-32-2 jdk-1.6-linux-32-3

    zxing jar包,支持jdk1.6,包括源码

    2. **适配JDK1.6**: - JDK1.6是Java的一个早期版本,尽管许多现代应用已经升级到更高版本,但有些旧系统可能仍依赖于它。此zxing jar包确保在1.6环境下能运行,避免了版本不兼容的问题。 - 为了适配JDK1.6,...

    jdk-6u45-linux-x64.zip_jdk-1.6u45_jdk-6u45_jdk-6u45-linux-x64_jd

    这个压缩包文件"jdk-6u45-linux-x64.zip"包含的是JDK 1.6.0_45(也被称为6u45或1.6u45)的64位Linux版本。JDK 1.6是Java平台标准版的一个重要版本,它提供了许多功能和性能改进,是许多企业级应用的基础。 JDK 1.6u...

    JDK-1.6-Windows-32位 官方

    压缩包中的文件`jdk-6u45-windows-i586.exe`是JDK 1.6更新45的Windows 32位安装程序。安装步骤通常包括: 1. 下载并运行安装程序。 2. 遵循安装向导的提示,选择安装路径和组件。 3. 设置环境变量,包括`JAVA_HOME`...

    jdk1.6官方版 jdk-6u45-windows-x64 下载

    java环境搭建 jdk6(包含jre)64位 jdk-6u45-windows-x64

    jdk-1.6-windows-32-2

    jdk-1.6-windows-32-1 jdk-1.6-windows-32-2 jdk-1.6-windows-32-3

Global site tag (gtag.js) - Google Analytics