美文网首页
AtomicInteger源码解析

AtomicInteger源码解析

作者: braveheart075 | 来源:发表于2019-01-30 20:15 被阅读0次

      我们在实现一个计数器的时候,很多情况下为了考虑线程安全,需要去加锁,防止计数器错乱,因为对于大多数count++来说,是两步操作。两个步骤的操作,多线程必然会产生错乱的现象。而atomicInteger这些concurrent包中的计数器却不会。下面我们来解析下源码。以AtomicInteger为例

      首先,看AtomicInteger类,有如下属性:

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;
    private volatile int value;
    

      这里说明下,由此看出这个类的底层是通过Unsafe来实现的。也就是说Unsafe是核心。有的人会问,这里为什么可以通过Unsafe.getUnsafe来获取Unsafe的实例,因为这个类在rt.jar 包中。系统的classloader加载。所以可以获取到。如果通过ApplicationClassloader加载,请参见下Unsafe源码,明显是会抛异常的。

      接下来看看valueOffset和value的作用:

    static {
            try {
                valueOffset = unsafe.objectFieldOffset
                    (AtomicInteger.class.getDeclaredField("value"));
            } catch (Exception ex) { throw new Error(ex); }
        }
    

      初始化valueOffset,这里的得到的是内存的偏移量。根据offset可以定位jvm中分配的内存地址。这里猜测就是定一个变量value,然后初始化把他的内存地址放入valueOffset变量里。
      这里还有一点比较重要,我们看下value的定义是volatile。volatile的意义就是copy出来的副本值始终和主内存中的值保持同步。即你永远读取的都是主内存中最新的值。这样子就保证了读的一致性。也就是不管多少个线程,你读取的value永远是一致的。

      通过以上解释,基本可以概括出来,通过value,valueOffset,unsafe这三个东西,实现了AtomicInteger的功能。

      构造函数比较简单,无参和有参。

    /**
         * Creates a new AtomicInteger with the given initial value.
         *
         * @param initialValue the initial value
         */
        public AtomicInteger(int initialValue) {
            value = initialValue;
        }
    
        /**
         * Creates a new AtomicInteger with initial value {@code 0}.
         */
        public AtomicInteger() {
        }
    

      这个相信大家在用AtomicInteger的时候,用过了构造函数了。有参的就是初始化下value的值,从初始化的值开始计数。

      下面看下实际用的比较多的方法,以getAndIncrement举例:

    /**
         * Atomically increments by one the current value.
         *
         * @return the previous value
         */
        public final int getAndIncrement() {
            return unsafe.getAndAddInt(this, valueOffset, 1);
        }
    

      这个就很好理解了,如果看了我之前写的unsafe的那篇文章就清楚了,这段代码的意思就是这个AtomicInteger对象的valueOffset内存偏移对应的值+1。那么对于unsafe.getAndAddInt做了些什么。这里要讲到CAS的概念了(compareAndSwap)。我们看下这段内在的逻辑:

    public final int getAndAddInt(Object var1, long var2, int var4) {
            int var5;
            do {
                var5 = this.getIntVolatile(var1, var2);
            } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    
            return var5;
        }
    

      很显然,一个cas的做法。看完这段代码,就知道为什么AtomicInteger效率高了,因为采用了乐观锁的概念。首先获取了对象this对应的offset内存偏移的值。也就是当前值:var5。接下来在while里进行判断,如果该offset内存偏移对应的值是var5,即没有被更改过,那么就更新成var5+var4。这个就是明显的乐观锁的概念。如果没更新成功,那么就直接返回啦。

      至此,AtomicInteger核心的思想解释完毕。大家可以试试自己去实现个AtomicInteger的类。

    相关文章

      网友评论

          本文标题:AtomicInteger源码解析

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