原子操作AtomicInteger 及辅助类 Unsafe

我们知道,一般给int类型数据赋值,比如说 int i = 3; i++; 其中的 i++ 操作,不是原子级别的,它包含了三个步骤,先取值 i = 3; 然后加一,即 3+1,最后,把得到的值4赋值给i,因此,如果是多线程并发操作,就很容易造成问题了。java中为了解决这个问题,提供了 AtomicInteger 类,仔细看,发现属于 Atomic 系列。我打开了 AtomicInteger 的源码,发现它继承了 Number, 我们知道 Number 操作也不是原子级别的,但 AtomicInteger 是,那么就来看看看 AtomicInteger 中有什么特别的操作。

发现,里面最醒目的是 private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); 也就是 Unsafe 类,其它的 Number 方法基本没有重写,那么看来问题就在于Unsafe类了。android 中 对于这个类,没办法直接使用,它属于系统级别的调用,如果想使用的,反射应该是可以的,这里只是讲一下个人的体会,水平有限,请见谅。 根据上面的代码,可以确定 Unsafe 对外提供了静态方法,返回一个 Unsafe 对象。继续看,发现头部有几行代码

    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    private static final long VALUE;

    static {
        try {
            VALUE = U.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (ReflectiveOperationException e) {
            throw new Error(e);
        }
    }

    private volatile int value;

我们知道,类被加载时,马上就会执行静态代码块,静态是随着类的加载而加载。我们注意看, AtomicInteger.class.getDeclaredField("value") 是使用了反射技术,获得 value 属性的 Field ,然后用 Unsafe 的 objectFieldOffset()方法,把它当做参数传了进去,得到一个 long 类型的 VALUE,这一静态代码块的意思,个人理解是根据反射获取field,然后通过Unsafe,根据 field 获取了属性 value 所在内存位置的指针地址值,类型是 long。

我们使用 AtomicInteger count = new AtomicInteger(0); 然后  int number = count.get(); 获取值,我们可以发现,AtomicInteger 是把数值记录在 value 属性中,而 VALUE 是value 在内存中的地址指针,那么可以大胆猜想是通过指针直接修改数据的值。好,继续看方法,

     public final int getAndSet(int newValue) {
        return U.getAndSetInt(this, VALUE, newValue);
    }

这个方法中, 看方法注释, Atomically sets to the given value and returns the old value. 意思是设置一个新值,返回原先的值。看看调用的 U.getAndSetInt(this, VALUE,newValue);方法,Unsafe 类,把当前对象类 AtomicInteger 和 value对应的内存中指针的值 VALUE 穿进去,同时把新值也传递进去,我们可以想象为 Unsafe 控制内存,根据前两个参数,找到了value值对应的地址值,然后把第三个参数赋值给指针对应的值,然后把原乡指针对应的值返回回来,这样就好理解了。

看第二个方法

    public final boolean compareAndSet(int expect, int update) {
        return U.compareAndSwapInt(this, VALUE, expect, update);
    }

有了上一个方法的经验,我们可以知道,比上面多了一个参数,按照方法名的意思,是比较数值并且设置数值,那么,同样道理,内存中找到value的指针,然后进行赋值替换,expect是原始值, update 是要更新的值,成功了就返回true,失败了就返回false。这里有个重点,比如说

    AtomicInteger count = new AtomicInteger();
    count.set(1);
此时,value的值为1,那么我们使用 compareAndSet()方法时,第一个参数就需要是1,这样才能替换成功,否则失败;

    private static void test() {
        AtomicInteger count = new AtomicInteger();
        count.set(1);
        boolean b = count.compareAndSet(1, 2);
        int num = count.get();
        System.out.println(" b= " + b + "  num= " + num);
    }
打印的结果是 b= true  num= 2。 如果修改数值,boolean b = count.compareAndSet(1, 2); 中的参数修改, boolean b = count.compareAndSet(3, 2);看看打印的值

    private static void test() {
        AtomicInteger count = new AtomicInteger();
        count.set(1);
        boolean b = count.compareAndSet(3, 2);
        int num = count.get();
        System.out.println(" b= " + b + "  num= " + num);
    }
打印的结果是 b= false  num= 1。

其实这两个方法就是核心,compareAndSet()方法,并发中CAS大概也是一样的道理吧。再看看其他方法,

    public final int getAndIncrement() {
        return U.getAndAddInt(this, VALUE, 1);
    }

    public final int getAndDecrement() {
        return U.getAndAddInt(this, VALUE, -1);
    }

很明显了,每次都加上一个固定的值,getAndIncrement() 可以理解为对应的是 i++,getAndDecrement() 对应的是 i--,最终都是调用 U.getAndAddInt()方法,只是传的值一个是1,一个是-1;要注意的是, getAndIncrement() 返回的是 原始的value值,这个方法操作后 value 是被更新了, 就好比是 int i = 0; int a = i++; 返回的值是 a,然后再 i = a; 把a的值赋值给i。 代码验证一下

    private static void test7() {
        AtomicInteger count = new AtomicInteger();
        count.set(1);
        int b = count.getAndIncrement();
        int num = count.get();
        System.out.println(" b= " + b + "  num= " + num);
    }

打印结果是  b= 1  num= 2。

    public final int getAndAdd(int delta) {
        return U.getAndAddInt(this, VALUE, delta);
    }
这个可以理解为上面方法的封装,自己可以控制要增加和减小的值,增加就传正数,减小就传负数。


如果我们想是想 ++i 这种操作呢,AtomicInteger 提供了一个巧妙的方法,那就是把值再加一次返回

    public final int incrementAndGet() {
        return U.getAndAddInt(this, VALUE, 1) + 1;
    }

    public final int decrementAndGet() {
        return U.getAndAddInt(this, VALUE, -1) - 1;
    }

这就相当于 int i = 0; int a = i++ + 1; i = a; 这是一个技巧,同理

    public final int addAndGet(int delta) {
        return U.getAndAddInt(this, VALUE, delta) + delta;
    }
也就好理解了。

    public final void lazySet(int newValue) {
        U.putOrderedInt(this, VALUE, newValue);
    }
这个方法就是直接赋值的,相当于 int i = 3; i =5; 这个直接记住就行了。

对于 AtomicInteger 类分析了大部分源码,了解到如何使用,这时候再看 java.util.concurrent.atomic 包下面其它的类 AtomicLong AtomicBoolean  AtomicReference 等等,都是一个道理。

猜你喜欢

转载自blog.csdn.net/Deaht_Huimie/article/details/86097171