• 原子类源码分析


    AtomicInteger

      Java从1.5开始提供非阻塞的线程安全的包装类,例如AtomicInteger、AtomicLong等,这些实现大同小异,这里以AtomicInteger为例。

    AtomicInteger的操作都基于Unsafe类,该类是JDK内部使用的一个工具类,提供硬件级别的原子操作,相关介绍戳https://www.jianshu.com/p/2e5b92d0962e。以下代码来自JDK1.8。
      首先来看一下成员变量:

     1 private static final Unsafe unsafe = Unsafe.getUnsafe();
     2 // value的内存偏移量
     3 private static final long valueOffset;
     4 
     5 static {
     6     try {
     7         // 获取value的内存偏移量
     8         valueOffset = unsafe.objectFieldOffset
     9             (AtomicInteger.class.getDeclaredField("value"));
    10     } catch (Exception ex) { throw new Error(ex); }
    11 }
    12 //
    13 private volatile int value;

       value是具体的值,该值是一个volatile变量,保证了内存可见性,另一个valueOffset变量是一个重要程度与value相当的一个变量,只要弄懂了这两个变量的作用,AtomicInteger就基本弄懂了,具体为什么需要这个变量,请看相关方法介绍。

      AtomicInteger类中的方法全部基于Unsafe,相关方法的作用请查看开头给出的链接或者自行google,这里只介绍一些方法来解释value与valueOffset的作用。

    1 public final int get() {
    2     return value;
    3 }
    4 
    5 public final void set(int newValue) {
    6     value = newValue;
    7 }

      除了get与set方法之外,所有的方法全部使用valueOffset,至于原因,我的理解是因为volatile会禁止JVM进行内存重排序等相关优化。所以接下来看一下其他方法:

    1 public final int getAndSet(int newValue) {
    2     return unsafe.getAndSetInt(this, valueOffset, newValue);
    3 }

      该方法是获得当前值然后将值设置为newValue,看一下Unsafe的方法:

    1 public final int getAndSetInt(Object var1, long var2, int var4) {
    2     int var5;
    3     do {
    4         var5 = this.getIntVolatile(var1, var2);// 原子获取地址为var1的地址加上偏移量var2的变量的值
    5     } while(!this.compareAndSwapInt(var1, var2, var5, var4));// 自旋CAS操作,成功将值设置为var4则返回true
    6 
    7     return var5;// 返回旧值
    8 }

      由上可以看出来该方法采用的是乐观锁机制,事实上Unsafe都是采用的乐观锁。
      set方法和lazySet方法的区别是原子包装类提及最多的问题,set采用volatile变量赋值保证了可见性但是会损失一定的性能(volatile写由于要加store-load屏障以及禁止重排序),lazySet采用的是Unsafe类的putOrderedInt方法,JDK官方对它的解释是,putOrderedInt方法之后会存在指令重排序,所以其可见性不能够保证,但是putOrderedInt使用的内存屏障(store-store)比volatile使用的内存屏障(store-load)性能更好,所以lazySet方式是保证了性能但是会损失可见性。
      总结一下,AtomicInteger是基于Unsafe类实现的,其中value存储的具体的值,主要作用是用于保证可见性而使用的,而valueOffset存储的是value的内存偏移量,在使用Unsafe类的方法时都是使用的该变量,主要作用是用于保证性能。

    AtomicIntegerArray

      AtomicIntegerArray的方法同样是基于Unsafe类,跟AtomicInteger的方法类似,这里不做介绍,仅对成员变量做分析。我们已经知道Unsafe类的操作是基于内存偏移量,所以重点了解AtomicIntegerArray如何计算数组元素的内存偏移量:

     1 private static final Unsafe unsafe = Unsafe.getUnsafe();
     2 // 数据的首地址
     3 private static final int base = unsafe.arrayBaseOffset(int[].class);
     4 // 数组中元素的单位偏移量
     5 private static final int shift;
     6 // 存储数组
     7 private final int[] array;
     8 
     9 static {
    10     // 获取数组中一个元素所占的字节数,由于是int所以占4个字节
    11     int scale = unsafe.arrayIndexScale(int[].class);
    12     if ((scale & (scale - 1)) != 0)
    13         throw new Error("data type scale not a power of two");
    14     // 2.获取单位偏移量
    15     shift = 31 - Integer.numberOfLeadingZeros(scale);
    16 }
    17 
    18 private long checkedByteOffset(int i) {
    19     if (i < 0 || i >= array.length)
    20         throw new IndexOutOfBoundsException("index " + i);
    21 
    22     return byteOffset(i);
    23 }
    24 
    25 // 1.计算元素的真实偏移量
    26 private static long byteOffset(int i) {
    27     return ((long) i << shift) + base;
    28 }

      在解释代码之前,先对了解一下数组地址分配。以int数组为例,int占4个字节,假设数组首地址是0,那么下标i=0的地址是0,i=1的地址是4,i=2的地址是8...,可以看出来,下标i跟地址的关系是addr=4*i=i<<2,如果数组首地址是base,则addr=(i<<2)+base。
      我们按照代码中标号的顺序解释:
      1. byteOffset方法是获取数组元素真实偏移量的,数组下标i左移shift位然后加上数组首地址就是数组首地址,这个shift我把它姑且叫做单位偏移量。
      2. numberOfLeadingZeros这个方法是获取参数的高位连续0的个数。scale的值是4,写成二进制就是0000 0000 0000 0000 0000 0000 0000 0100,最后numberOfLeadingZeros的返回值是29,因为1前面有29个0,所以最后计算出来shift=2。
      经过上面的解释大家应该已经知道了AtomicIntegerArray是如何计算数组中元素的地址的了。这里总结一下,我们把前面的addr=4*i+base做一个变形,假设数组元素占2的shift次幂个字节那么addr=[(2^shift)*i]+base=(i<<shift)+base,这就是一个所有基本类型通用的公式,也就是代码中byteOffset方法。

  • 相关阅读:
    阿里云ECS服务器安装docker
    ActiveMq-常见的问题
    ActiveMq-基础知识
    java语言概述
    【日语词汇类编】自然与人:气候与环境
    【日语词汇类编】传媒与娱乐:大众传播媒介
    机器学习数学基础-线性代数
    掌握机器学习数学基础之概率统计
    机器学习理论篇:机器学习的数学基础
    计算机基础知识
  • 原文地址:https://www.cnblogs.com/ouhaitao/p/11242721.html
Copyright © 2020-2023  润新知