原子类AtomicInteger实现浅析

 

1 前言

在多线程程序中,如果多个线程同时更新一个共享变量,可能会出现预料之外的奇怪的值。普通的变量无法在多线程下做到可见性、一致性、原子性,也就无法保证线程安全。在JDK的java.util.concurrent.atomic包中提供许多原子操作类,它们可以简单、 高效、安全地更新一个变量。现在介绍其中的基本类型的原子操作类。

基本数据类型有booleancharbyteshortintlongfloatdouble 这8种,但其原子类只有AtomicBoolean AtomicInteger AtomicLong.其他未包含的基本类型可通过一些操作转换成原子类型。如:只取AtomicInteger 的低32位(低位的两字节)可作为short 的原子类,将double放大10n倍将其变为整数,然后就可使用AtomicLong原子类。这3个类的实现基本相同,这里以AtomicInteger为例作说明

2 方法说明

主要API如下

//以原子方式获取旧值并设置新值
public final int getAndSet(int newValue)
//以原子方式获取旧值并自增1
public final int getAndIncrement()
//以原子方式获取旧值并自减1
public final int getAndDecrement()
//以原子方式获取旧值并给当前值加delta
public final int getAndAdd(int delta)
//以原子方式给当前值自增1并获取新值
public final int incrementAndGet()
//以原子方式给当前值自减1并获取新值
public final int decrementAndGet()
//以原子方式给当前值加上delta并获取新值
public final int addAndGet(int delta)

 

3 实现原理

上面的这些API都会使用到Unsafe类的相关方法,可以说原子类的实现关键在于Unsafe。AtomicInteger使用一个int类型的成员变量value来表示原子类所代表的数值, value使用volatile关键字修饰,在多线程环境中能保证其可见性。Unsafe类可以在本地内存中直接操作value.

    private volatile int value;

 

AtomicInteger有两个构造方法,带参构造方法用参数指定初始值,无参数构造方法将初始值设为0

    public AtomicInteger(int initialValue) {
        value = initialValue;
    }
    public AtomicInteger() {
    }

 

Unsafe.objectFieldOffset(Field)方法能根据成员变量的反射表示形式Filed获取内存中对象起始位置objAddr至此成员变量位置fieldAddr的相对偏移量offset,在已知对象起始位置和成员变量相对偏移量offset时,可计算出成员变量在内存中的绝对地址,即fieldAddr=objAddr+offset。Unsafe是系统底层类,它的所有方法几乎都是(native)本地方法,本地方法是用C/C++实现的,C/C++方法根据变量的内存地址可直接操作变量value,它能越过访问修改符的访问限制。Unsafe操作成员变量的方法签名几乎都是XXX(Object obj,long offset ,? x),它要求调用者同时提供对象的引用obj(知道对象的引用也就能获取对象的起始地址)及成员变量的相对偏移量offset,根据这两个已知量即可求出此成员变量在内存中的绝对地址。

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); }
}

 

原子类是基于CAS实现的,具体表现在原子类API的调用链底层使用Unsafe.compareAndSwapXX方法.

getAndSet方法使用Unsafe.getAndSetInt实现,而getAndSetInt方法体主要是一个while自旋循环体,while循环体的核心方法是compareAndSwapInt方法,这是一个CAS方法。

//AtomicInteger
public final int getAndSet(int newValue) {
    return unsafe.getAndSetInt(this, valueOffset, newValue);
}
//Unsafe
public final int getAndSetInt(Object o, long offset, int newValue) {
    int v;
    do {
        v = getIntVolatile(o, offset);//先获取原来的value
        
  //如果v与此刻执行compareAndSwapInt方法时的value不等,则表明
  //value被其他线程修改了,此处compareAndSwapInt会失败,将继续自旋重试
    } while (!compareAndSwapInt(o, offset, v, newValue));
    return v;
}
//Unsafe 
public final native boolean compareAndSwapInt(Object o, long offset,  int expected,  int x);//本地方法

 

其他方法也基本上也是基于这种CAS机制实现,如incrementAndGet()方法。

//AtomicInteger
public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
//Unsafe
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;
}


posted @ 2020-05-06 21:33  蜀中孤鹰  阅读(520)  评论(0编辑  收藏  举报