随笔分类
AtomicInteger
基于CAS来实现线程安全的
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();//使用了Unsafe类
private static final long valueOffset;
static { //静态代码块,结合类加载,不难想出它这样做的目的
try {//可以看出,AtomicInteger是通过Unsafe来操作内存的
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));//得到value的偏移地址,加上对象的起始地址,不就可以直接来操作内存空间了?
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;//保存AtomicInteger中的值,使用volatile,保证其可见性,默认构造初始值为0
来看看AtomicInteger是如何通过CAS来保证其自增的原子性
源码解读
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;//调用了unsafe的方法,这里的this是指AtomicInteger对象,实质上就是旧的预估值 + 1
}
Unsafe.class
public final int getAndAddInt(Object var1, long var2, int var4) {//这里就是所谓的CAS了
int var5;
do {
var5 = this.getIntVolatile(var1, var2);//通过this和offset来找到value的值,可以看出,Var5其实就是旧的预估值
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));//通过var1和var2拿到内存中最新的value值,var5即旧的预估值,二者进行比较,相同的话则将内存中的值进行自增操作(即Var5 + Var4),并且返回true,不同时返回false,继续循环 -->重试机制,以来更新旧的预估值,这样就能保证当发生内存数据的更改时总能通过do-while来拿到内存中最新的那个值,这也是为何什么要使用volatile的原因,以来保证内存数据的可见性,JMM
return var5; //把旧的预估值返回
}
public native int getIntVolatile(Object var1, long var2);//JNI
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);//JNI
总结
CAS乐观锁,综合性能较好
CAS获取共享变量时,为了保证该变量的可见性,需要使用volatile修饰
结合CAS和volatile可以实现无锁并发,适用于竞争不激烈、多核CPU的场景下
- 没有使用synchronized,所以线程不会陷入阻塞,所以并发效率会高一些
- 竞争比较激烈时,重试会比较频繁,显然会影响并发效率