AtomicInteger类处于java.util.concurrent.atomic包下,与其他原子操作的类一样,底层都是采用CAS机制,调用了Unsafe类的CAS方法实现的。我们先分析一下AtomicInteger类的源码,再来分析CAS机制的种种。
public class AtomicInteger extends Number implements java.io.Serializable
继承了 Number, 这主要是提供方法将数值转化为 byte, double 等方便 Java 开发者使用; 实现了 Serializable, 为了网络传输等的序列化用, 编码时最好手动生成序列化 ID, 让 javac 编译器生成开销大, 而且可能造成意想不到的状况。
//序列化标识id
private static final long serialVersionUID = 6214790243416807050L;
//Unsafe类的一个实例,提供一些不安全的操作的方法用于直接操作内存,一般不会在自己的程序中使用该类
//在这里主要用到其中的objectFieldOffset、putOrderedInt、compareAndSwapInt方法
private static final Unsafe unsafe = Unsafe.getUnsafe();
//value成员属性的内存地址相对于对象内存地址的偏移量
private static final long valueOffset;
////初始化valueOffset,通过unsafe.objectFieldOffset方法获取成员属性value内存地址相对于对象内存地址的偏移量
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//int的值,volatile修饰,保证线程之间的可见性
private volatile int value;
Unsafe类是一个JDK内部使用的专属类,我们自己的应用程序无法直接使用Unsafe类。通过观察源码,可以知道获得Unsafe实例的方法是调动其工厂方法getUnsafe(),源码如下:
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
它会检查调用getUnsafe()函数的类,判断调用这的类加载器是否是系统类加载器(系统类加载器为null),如果不是就直接抛出异常,拒绝工作,这也是为什么我们写的程序没法直接使用这个类的原因。
//int参数类型的构造函数
public AtomicInteger(int initialValue) {
value = initialValue;
}
//无参构造函数,初始值是0
public AtomicInteger() {
}
/**
* 获取int值
*/
public final int get() {
return value;
}
/**
* 设为指定值
*/
public final void set(int newValue) {
value = newValue;
}
/**
* 最终设为指定值,但其它线程不能马上看到变化,会延时一会
*/
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
}
/**
* 以原子方式设置为给定值,并返回旧值
*/
public final int getAndSet(int newValue) {
//乐观锁,非阻塞同步方式,循环调用compareAndSet,也就是自旋,直到成功
for (;;) {
int current = get();
//CAS操作,期待值current与内存中的值比较,相等的话设为newValue值
//否则下个循环,调用get()获取current值,继续执行CAS操作直到成功
if (compareAndSet(current, newValue))
return current;
}
}
/**
* CAS操作,现代CPU已广泛支持,是一种原子操作;
* 简单地说,当期待值expect与valueOffset地址处的值相等时,设置为update值
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
* 弱比较
*/
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
//调用unsafe.getAndAddInt方法进行数值操作
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1);
}
//调用unsafe.getAndAddInt方法进行数值操作
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
//调用unsafe.getAndAddInt方法进行数值操作
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
//调用unsafe.getAndAddInt方法进行数值操作
public final int decrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
}
//调用unsafe.getAndAddInt方法进行数值操作
public final int addAndGet(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
/**
* 原子操作,自增,返回旧值
*/
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
/**
* 原子操作,自减,返回旧值
*/
public final int getAndDecrement() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return current;
}
}
/**
* 原子操作,加上一个数,返回旧值
*/
public final int getAndAdd(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return current;
}
}
/**
* 原子操作,自增,返回新值
*/
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
/**
* 原子操作,自减,返回新值
*/
public final int decrementAndGet() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return next;
}
}
/**
* 原子操作,加上一个数,返回新值
*/
public final int addAndGet(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return next;
}
}
/**
* Returns the String representation of the current value.
* @return the String representation of the current value.
*/
public String toString() {
return Integer.toString(get());
}
public int intValue() {
return get();
}
public long longValue() {
return (long)get();
}
public float floatValue() {
return (float)get();
}
public double doubleValue() {
return (double)get();
}
}
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;
}
第一个参数var1为给定的对象,var2为对象内的偏移量(其实就是一个字段到对象头部的偏移量,通过这个偏移量可以快速定位字段),var4表示期望值,var5表示要设置的值。如果指定的字段的值等于var4,那么就会把它设置为var5。
CAS全拼又叫做compareAndSwap,从名字上的意思就知道是比较交换的意思。它包含 3 个参数 CAS(V,E,N),V表示要更新变量的值,E表示预期值,N表示新值。仅当 V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做两个更新,则当前线程则什么都不做。最后,CAS 返回当前V的真实值。
CAS是一种乐观锁,而且是一种非阻塞的轻量级的乐观锁。当一个线程想要获得锁,对方会给一个回应表示这个锁能不能获得。在资源竞争不激烈的情况下性能高,相比synchronized重量锁,synchronized会进行比较复杂的加锁,解锁和唤醒操作。
(1) ABA问题
* 问题描述:CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。
* 解决办法:
a. 用CAS的一个变种DCAS,DCAS是对于每一个V增加一个引用的表示修改次数的标记符。对于每个V,如果引用修改了一次,这个计数器就加1,然后再这个变量需要更新的时候,就同时检查变量的值和计数器的值。都符合条件才可以修改V值。
b. 通过将标记与要进行CAS操作的每个值相关联,并原子地更新值和标记。也就是一旦V第一次被使用,就不会再重复使用,如有需要则分配新的V。垃圾收集器可以检查V,保证其不被循环使用,直到当前的访问操作全部结束。使用 AtomicStampedReference 来解决CAS中的ABA问题,它不再像compareAndSet方法 中只比较内存中的值也当前值是否相等,而且先比较引用是否相等,然后比较值是否相等。
c. 设置一个版本号,每次修改都会修改版本号,每次对比的时候不仅对比值,还要对比版本号来保证数据没被修改。
(2) 自旋时间长导致开销大
* 问题描述:自旋 CAS 如果长时间不成功,会给 CPU 带来非常大的执行开销。
* 解决办法:如果JVM能支持处理器提供的 pause 指令那么效率会有一定的提升,pause 指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使 CPU 不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation:内存顺序冲突一般是由伪/假共享引起,假共享是指多个 CPU 同时修改同一个缓存行的不同部分而引起其中一个CPU的操作无效,当出现这个内存顺序冲突时,CPU必须清空流水线)而引起 CPU 流水线被清空(CPU pipeline flush),从而提高 CPU 的执行效率。
通过本篇博文,对原子包java.util.concurrent.atomic下的AtomicInteger类进行了分析,其他的类也比较类似,都是底层采用了CAS机制完成各种操作。也学习了CAS的原理和存在的问题以及解决办法。
更多精彩内容,敬请扫描下方二维码,关注我的微信公众号【Java觉浅】,获取第一时间更新哦!