AtomicInteger 源码分析阅读

阅读java源码可能是每一个java程序员的必修课,只有知其所以然,才能更好的使用java,写出更优美的程序,阅读java源码也为我们后面阅读java框架的源码打下了基础。阅读源代码其实就像再看一篇长篇推理小说一样,不能急于求成,需要慢慢品味才行。这一系列的文章,记录了我阅读源码的收获与思路,读者也可以借鉴一下,也仅仅是借鉴,问渠那得清如许,绝知此事要躬行!要想真正的成为大神,还是需要自己亲身去阅读源码而不是看几篇分析源码的博客就可以的。

正文

最近在看JAVA1.8线程池源码的时候,发现对于AtomicInteger类的使用不太熟悉,所以就专门研究了一下,有一些收获。AtomicInteger是juc(java.util.concurrent)包下提供的一个可以原子性操作Integer对象的类。通过它,我们可以很方便的对Integer进行线程安全的加、减、改值等操作。其原理是使用的CAS无锁算法。

 private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    //JAVA实现CAS算法的类,整个类有关线程安全的操作,都是借助它来实现。
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    //变量value的内存首地址的偏移量。
    private static final long valueOffset;
    //静态代码块,在类加载时运行
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    //存放int值
    private volatile int value;

1.unsafe 其实就是负责与CAS相关的操作实现,这里需要提一下的是Unsafe是不符合JAVA标准的,官方不推荐大家在编写自己的中使用它,因为它引入了和C++中指针相似的东西,但可以借用java.util.concurrent.atomic包下的类来使用。
2.valueOffset的值是变量value的内存首地址的偏移量。,它在AtomicInteger被加载时就被赋值了。
3.value其实就是存放实际值的变量,它被volatile 关键字修饰,说明对于其他线程是可见的。

可能大家还是不大能够了解valueOffset到底是个什么东西,有什么用,当时我也在这里卡了很久,接下来我来讲一下自己的分析过程。首先从字面意思来讲,应该是和value有关的一个变量。再看它是由objectFieldOffset()这个方法的返回值进行赋值的。我们查看一下objectFieldOffset()的声明定义:
objectFieldOffset

发现并没有任何描述,但是通过百度发现说是value在内存中首地址。出于严禁的角度,我就写了一个测试来看看:
public class Main {

    public static void main(String[] args) {
        AtomicInteger a=new AtomicInteger(1);
        AtomicInteger b=new AtomicInteger(1);
        int c=a.addAndGet(5);
        b.addAndGet(4);
        System.out.println(a.get());
        System.out.println(c);

    }
}

addAndGet()方法的定义与声明


addGet()

这个测试中,我实例化了两个AtomicInteger 对象:a和b,同时在调用的方法addAndGet()中打了断点。
最后debug发现,两个变量的valueOffset值都为12。说明valueOffset就是value内存首地址的偏移量。到这里或许大家还想问,这个偏移应该是针对于什么的偏移呢?你偏移量总得有个参照位置吧。这里就要回到我们valueOffset的定义与赋值了。

private static final long valueOffset;
    //静态代码块,在类加载时运行
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

我们可以看到,valueOffset 是一个静态常量,并且在类加载时就被赋值了。那么这个类在编译后的字节码是一定的,但是存放对象的首地址是随机的,所以这里的偏移应该是相对于对象实例的首地址。如果大家看不明白,请看下面这幅图,这幅图简介明了的描述了valueOffset 的含义。


valueOffset 的含义

再联系到AtomicInteger在调用unsafe接口时,都将this和valueOffset 作为传入参数(如下图),我们可以了解到unsafe实现的CAS算法是依赖底层实现的(因为方法声明有native关键字),我们为其提供具体的内存地址,即this+valueOffset ,就可以让其找到要进行原子性操作的变量value,并进行修改。


compareAndSet

unsafe中的一个方法声明
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 在对J.U.C包的源码分析之前,首先介绍下一个比较重要的概念-CAS(Compare-and-Swap)。在J.U...
    泰迪的bagwell阅读 8,005评论 0 20
  • Java8张图 11、字符串不变性 12、equals()方法、hashCode()方法的区别 13、...
    Miley_MOJIE阅读 3,731评论 0 11
  • 独自去一个陌生的城市,没出发之前,和任何人说自己一个人去,你好厉害,注意安全,找同伴......如果第一次独自出去...
    噗梅阅读 196评论 0 0
  • 今日三朵玫瑰 1.早起 2.观看穿刺视频 3.准备明天考试 【幸福实修99天目标】 1.臣服父母,每天做打卡。 2...
    叶青丁当妈阅读 396评论 1 1
  • 那是个夏末秋初的中午,与朋友在路旁等车。 或许是“夏”感觉到季节即将终结,居然越发燥热,或许是“夏”想在最后关头发...
    孤独一刀阅读 326评论 0 5