AtomicInteger 是一个支持原子操作的 Integer 类,就是保证对AtomicInteger类型变量的增加和减少操作是原子性的,不会出现多个线程下的数据不一致问题。
AtomicInteger底层使用了CAS乐观锁的思想。CAS一直不太懂,今天正好好好研究一下。
下面先分析下源码:
1,成员变量:
private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; private volatile int value; //使用volatile修饰,保证获取的都是最新值
2,构造函数:一个默认构造函数,一个带着value参数的构造函数
public AtomicInteger(int var1) { this.value = var1; } public AtomicInteger() { }
3,方法
public final int get() { return this.value; //因为value是volatile,所以获取的值都是最新的. } public final void set(int var1) { this.value = var1; //直接对value设值 } public final int getAndSet(int var1) { return unsafe.getAndSetInt(this, valueOffset, var1);//调用unsafe类的方法}
unsafe的getAndSetInt方法:
public final int getAndSetInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); //这是一个native方法,返回旧值 } while(!this.compareAndSwapInt(var1, var2, var5, var4)); //这是一个native方法,如果新值代替旧值失败,自旋尝试直到成功 return var5; }
加减方法
public final int getAndIncrement() { //加1,返回旧值 return unsafe.getAndAddInt(this, valueOffset, 1); } public final int getAndDecrement() { //减1,返回旧值 return unsafe.getAndAddInt(this, valueOffset, -1); } public final int getAndAdd(int var1) { //加var1,返回旧值 return unsafe.getAndAddInt(this, valueOffset, var1); } public final int incrementAndGet() { //加1,返回新值 return unsafe.getAndAddInt(this, valueOffset, 1) + 1; } public final int decrementAndGet() { //减1,返回新值 return unsafe.getAndAddInt(this, valueOffset, -1) - 1; } public final int addAndGet(int var1) { //加var1,返回新值 return unsafe.getAndAddInt(this, valueOffset, var1) + var1; }这些函数都是调用的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; }和前面getAndSetInt不同的是新值的不同,这次是增加var4.都是使用的CAS算法来保证原子性.
public final int getAndUpdate(IntUnaryOperator var1) {//IntUnaryOperator是功能接口 int var2; int var3; do { var2 = this.get();//获得现在最新值 var3 = var1.applyAsInt(var2);//将var2转化为int型 } while(!this.compareAndSet(var2, var3));//调用compareAndSet return var2;//返回更新之前的值 } public final int updateAndGet(IntUnaryOperator var1) { int var2; int var3; do { var2 = this.get(); var3 = var1.applyAsInt(var2); } while(!this.compareAndSet(var2, var3)); return var3;//返回更新之后的值 } public final int getAndAccumulate(int var1, IntBinaryOperator var2) { int var3; int var4; do { var3 = this.get(); var4 = var2.applyAsInt(var3, var1); } while(!this.compareAndSet(var3, var4)); return var3; } public final int accumulateAndGet(int var1, IntBinaryOperator var2) { int var3; int var4; do { var3 = this.get(); var4 = var2.applyAsInt(var3, var1); } while(!this.compareAndSet(var3, var4)); return var4; }
IntUnaryOperator和IntBinaryOperator都是功能接口,使用者可以自己实现这两个接口,自己定义applyAsInt函数,来决定更新的值如何变化,不仅仅是加减运算.
两者不同的是,IntUnaryOperator的applyAsInt只有一个参数,就是旧值.而IntBinaryOperator的applyAsInt有两个参数,一个是旧值,一个是自己设定的值.
compareAndSet方法也是调用CASpublic final boolean compareAndSet(int var1, int var2) { return unsafe.compareAndSwapInt(this, valueOffset, var1, var2); }
还有一点不明:valueOffset是什么?
下面看看这个:
static { try { valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value")); } catch (Exception var1) { throw new Error(var1); } }当前值存放的内存地址可以通过valueOffset来确定。实际上是“value字段相对Java对象的起始地址的偏移量”.
原来是这样的:compareAndSwapInt通过当前对象和偏移量来找到value的当前值,再和之前获得的旧值比较,看是否相等,相等则更新,返回true,不相等返回false.
总结:这里面最重要的就是:value值使用volatile修饰保证可见性,然后是使用CAS保证对AtomicInteger的修改是线程安全的.
CAS:比较CPU内存上的值是不是当前值current,如果是就换成新值update,如果不是,说明获取值之后到设置值之前,该值已经被别人先一步设置过了,此时如果自己再设置值的话,需要在别人修改后的值的基础上去操作,否则就会覆盖别人的修改,所以这个时候会直接返回false,再进行无限循环,重新获取当前值,然后再基于CAS进行更新操作。