AtomicInteger
AtomicInteger是一个原子的自增操作类
分析一下AtomicInteger的源码
先看看它的成员变量
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
private volatile int value;
Unsafe类是jdk内部使用的类,和硬件打交道用来做cas原子性操作的
value是自增的变量
valueOffset是value这个变量在这个类的偏移量位置,通过cas操作修改偏移量
看看getAndIncrement方法源码
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
内部调用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;
}
一个do-while循环,getIntVolatile是一个native方法,获取当前对象里面value的偏移量的值
然后调用compareAndSwapInt方法,compareAndSwapInt先比较再修改,这就是cas操作,cas成功返回ture,失败返回false,var1是当前对象this,var2是原来value的偏移量,var5是原来value的值,var5+var4,是值,cas操作会锁定一块内存,先比较是不是原来的值,如果是就修改,如果不是是返回false,如果false就会进入下一次循环知道修改成功为止
Atomic原子类CAS的缺点
1.ABA问题:某个值一开始是A,后来变成B,然后又变成了A,比较的时候发现还是A,但是其实被修改过,atomic包里有AtomicStampedReference类,就是会比较两个值的引用是否一致,如果一致,才会设置新值
2.无限循环问题:并非冲突过高的,一直修改失败无限循环重试,导致循环很多次才能成功,JDK1.8里面引入LongAddr来解决,分段CAS思路
3.多变量原子问题:AtomicInterger只能保证一个变量的原子性,多个变量原子性可以用AtomicReference来保证,这个类封装自定义对象,检查这个对象的引用是不是同一个
LongAddr
为了避免大量并发对一个值修改导致大量失败重试,把值拆成多个cell,先对一个cell做cas操作,如果发现自选次数太多就会换到另一个cell做cas操作,最后把所有cell的值加起来就是最后的结果了,比如要从0自增,10个cell,每个cell自增了5次,然后所有cell加起来就是最后的结果50了,默认是两个cell,每次cell扩大是翻倍,下图表示
AtomicReference
AtomicReference是解决多个属性的cas操作的,AtomicReference可以传一个自定义的对象,每次判断引用还是不是之前的那个
public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
通过unsafe.compareAndSwapObject方法,新值和就值的引用还是不是同一个,是就修改成功,不是就返回失败
AtomicStampedReference
AtomicStampedReference是解决aba问题,加了版本号的机制
里面多了一个stamp,每次cas的时候都会比较久值 还会比较版本号是不是一样,不一样就算失败