在多线程程序中,如果多个线程同时更新一个共享变量,可能会出现预料之外的奇怪的值。普通的变量无法在多线程下做到可见性、一致性、原子性,也就无法保证线程安全。在JDK的java.util.concurrent.atomic包中提供许多原子操作类,它们可以简单、 高效、安全地更新一个变量。现在介绍其中的基本类型的原子操作类。
基本数据类型有boolean
、 char
、 byte
、 short
、int
、long
、 float
、double
这8种,但其原子类只有AtomicBoolean AtomicInteger AtomicLong.其他未包含的基本类型可通过一些操作转换成原子类型。如:只取AtomicInteger 的低32位(低位的两字节)可作为short
的原子类,将double
放大10n倍将其变为整数,然后就可使用AtomicLong原子类。这3个类的实现基本相同,这里以AtomicInteger为例作说明
主要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)
上面的这些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;
}