Java AtomicInteger 原理

Java AtomicInteger 原理

AtomicInteger是一个支持原子操作的int封装类,提供了原子性的访问和更新操作
其底层是通过volatile和CAS实现的,其中volatile保证了内存可见性,CAS算法保证了原子性

volatile

volatile是用于保证可见性的关键字,当变量被声明为volatile后,每次操作该变量都会强制将修改的值立即写入主存(volatile还能够保证一定的有序性,但这里不做介绍)

在AtomicInteger内部可以看到以volatile修饰的value字段用来记录数值,以保证可见性

private volatile int value;

CAS

CAS(Compare And Swap)即比较并交换,表示的是一系列操作的集合,获取当前数值进行一些运算,利用CAS指令试图进行更新。如果当前数值未变,代表没有其他线程进行并发修改,则成功更新。否则可能出现不同的选择,要么进行重试,要么就返回一个成功或者失败的结果

CAS底层依赖于CPU特定的指令集:

  • x86上使用cmpxchg指令
  • 精简指令集构架CPU通常依靠一对指令,如"load and reserve"和"store conditional"

源码解析

从AtomicInteger的内部属性可以看出,它依赖于Unsafe提供的一些方法来实现CAS操作

private static final Unsafe unsafe = Unsafe.getUnsafe();

创建对象时,通过unsafe.objectFieldOffset方法获取到内部变量value的内存偏移地址

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;

Unsafe类可以实现一些不安全的操作,通常这些操作涉及到指针,如分配释放内存、定位属性在内存中的位置、挂起恢复线程、CAS操作等,由于使用风险较大因此Java9中限制了Unsafe类的使用并移除了一些功能

然后我们首先看下compareAndSet方法,该方法作用是将当前值与预期值进行比较,相等的话设置为更新值,更新成功返回true,失败返回false

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

可以看到内部调用了Unsafe中提供的compareAndSwapInt这个native方法,该方法会通过当前值的内存地址(offset)获得当前值的实际值与期望值(expected)进行对比,相同的话就替换为更新值(x),该方法基于CPU的特定的指令实现

public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);

接着看下原子更新方法,比如getAndIncrement方法,该方法作用是将当前值加一并返回加一前的值,类似i++

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

内部调用了Unsafe的getAndAddInt方法,该方法内循环的作用是CAS操作失败的重试逻辑
在多线程并发时,CAS操作不一定能够成功,因此通过不停的调用compareAndSwapInt直到返回true时退出循环,来实现非阻塞的原子性操作

public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset);
    } while (!compareAndSwapInt(o, offset, v, v + delta));
    return v;
}

其他原子更新方法原理相同,比如incrementAndGet,与getAndIncrement相比就是i++与++i的区别

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

参考

Java核心技术面试精讲
https://zhuanlan.zhihu.com/p/87141904
https://www.jianshu.com/p/51189b4ef6c1
https://juejin.im/post/5cc00f77f265da0359487485
https://fangjian0423.github.io/2016/03/16/java-AtomicInteger-analysis/

发布了174 篇原创文章 · 获赞 119 · 访问量 55万+

猜你喜欢

转载自blog.csdn.net/lj402159806/article/details/103672391