juc-atomic原子类之二:基本类型原子类AtomicInteger(AtomicLong、AtomicBoolean)

一、AtomicInteger简介

AtomicInteger, AtomicLong和AtomicBoolean这3个基本类型的原子类的原理和用法相似。以AtomicInteger对基本类型的原子类进行介绍。

二、AtomicInteger源码分析

2.1、类图结构

2.2、数据结构

public class AtomicInteger extends Number implements java.io.Serializable {
    // unsafe对象,可以直接根据内存地址操作数据,可以突破java语法的限制
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    // 存储实际的值
    private volatile int value;
    // 存储value属性在AtomicInteger类实例内部的偏移地址
    private static final long valueOffset;
    static {
        try {
            // 在类初始化的时候就获取到了value变量在对象内部的偏移地址
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
}

 

2.3、AtomicInteger中的lock

使用Unsafe的cas。

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {    
      try {        
        valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));    
      } catch (Exception ex) { 
            throw new Error(ex);
      }
}
private volatile int value; //注意value变量的volatile属性

功能说明:
1、获取Unsafe对象
2、valueOffset是通过反射获取AtomicInteger类中value在内存中的位置。

接着看cas调用:

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

功能:
通过CAS算法,设置value的值。CAS算法就是先比较value的值和expect的值是否相同,如果相同就设置update。如果不相同则设置失败。

注意下,上面对value值修改时,是通过value的valueOffset来进行的。这样就保证了,对value的原子操作。

2.4、成员变量

    private static final Unsafe unsafe;
    private static final long valueOffset;
    private volatile int value;

2.5、构造函数

    public AtomicInteger(int i)
    {
        value = i;
    }

2.6、赋值元素

    public final void set(int i)
    {
        value = i;
    }

2.7、查询元素

    public final int get()
    {
        return value;
    }

 

成员变量和方法都介绍完了后,我们看看如何工作的:

(1)首先内部持有一个unsafe对象的实例,Atomic原子类底层的操作都是基于unsafe对象来进行的

(2)然后有一个volatile int value变量,这个value就是原子类实际数值,使用volatile来修饰volatile可以保证并发中的可见性有序性(这里之前讲过volatile可以保证可见性和有序性,不记得的要回去重新看一下哦)

(3)还有一个valueOffset,看看这段代码,其实就是获得value属性在AtomicInteger对象内部的偏移地址的:

valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));

这个value属性相对于AtomicInter对象的内部偏移量存储在valueOffset中,我们之前讲过的,通过unsafe类是直接在内存级别去给变量赋值的。这里啊,我们再回顾一下unsafe值怎么从内存级别操作数据的:

首先要知道你要操作对象的内存地址,也就是AtomicInteger对象引用指向的内存地址

其次是要知道value属性在对象内部的偏移量offset,就可以通过(对象地址 + offset偏移量)直接找到value变量在内存的地址是多少,然后就可以直接给这块内存赋值了。

 

 

 


其实Atomic基础类型的原子类是对基础的类型进行了一下包装而已,使得他们是线程安全的。比如AtomicInteger要对int进行包装,所以它内部肯定是有一个属性来存储int的值的。至于它其他两个属性valueOffset、unsafe是辅助实现并发安全的属性。

2.8、自增自减

    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

 

AtomicInteger的getAndIncrement()方法源码很简单,底层就是基于unsafe.getAndAddInt包装了一下,让我们继续看一下unsafe.getAndAddInt方法源码:

    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;
    }

 

(1)首先o + valueOffset得到value变量在内存中的地址,然后根据地址直接取出value在主内存值,这个值记录为var5(期望值)
(2)根据 o + offsetSet地址偏移量,expected期待的值跟当前内存的值进行对比,如果相等则CAS操作成功内存的值修改为 var5 + x
(3)如果值不相等,则进入下一次循环,直到CAS操作成功为止。
(4)由于使用了volatile 修饰符修饰了value,所以一旦修改了别的线程能立马可见、同时volatile还是用内存屏障确保有序性
(5)所以上面的CAS操作确保了原子性,通过volatile确保可见性、有序性;线程安全的三个特性都满足了,上面的操作就是线程安全的。

调用unsafe的getAndAddInt()方法,最后还是走到了unsafe的compareAndSwapInt方法里面。底层都是基于unsafe的CAS操作来保证原子性的,然后有使用volatile来修饰变量,保证了可见性和有序性,这样它就是线程安全的。

类似的方法还有:

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

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

 

三、AtomicBoolean底层原理

AtomicBoolean 基本属性分析

public class AtomicBoolean implements java.io.Serializable {
    // unsafe对象,可以直接根据内存地址操作数据,可以突破java语法的限制
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    // 存储实际的值
    private volatile int value;
    // 存储value属性在AtomicInteger类实例内部的偏移地址
    private static final long valueOffset;
    static {
        try {
            // 在类初始化的时候就获取到了value变量在对象内部的偏移地址
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
}

 

 属性和AtomicInteger相同,AtomicBoolean用了一个小技巧,看构造函数源码:

public AtomicBoolean(boolean initialValue) {
    // 当传入initialValue为true的时候value = 1 , false的时候value = 0
    value = initialValue ? 1 : 0;
}

 

AtomicBoolean 底层就是使用一个int类型来表示true和false的,当value = 1的时候表示true,当value = 0的时候表示false
然后继续看一下get()的源码
直接就是判断value != 0 , 当value = 1则返回true,value = 0 返回false

public final boolean get() {
    return value != 0;
}

 

底层就是将true 转成 1,将false转成 0,然后还是调用unsafe的compareAndSwapInt方法去执行CAS操作,这个我们在上面将AtomicInteger的时候已经讲过了。

 

posted on 2013-12-07 22:58  duanxz  阅读(620)  评论(0编辑  收藏  举报