AtomicInteger源码解析

  我们在实现一个计数器的时候,很多情况下为了考虑线程安全,需要去加锁,防止计数器错乱,因为对于大多数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源码解析)