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的时候已经讲过了。