一、 什么是CAS?
CAS,英文名称:compare and swap,比较交换。主要是应用在AtomicInteger类中,其基本原理为:真实值和期望值相同,则修改成功,否则,修改失败。其底层调用Unsafe类,Unsafe是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据。Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中CAS操作的执行依赖Unsafe类的方法。其中变量value用了volatile修饰,保证了多线程之间的内存可见性。CAS是一条CPU并发原语,通过Unsafe类,让JVM帮我们是先出CAS汇编指令,能保证原子性。所以运用CAS能够保证线程安全。
其运用主要通过compareAndSet(int expect, int update)方法,其底层如下:
变量分配是当前值,值的地址,预期值,更新值
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
Unsafe类中的compareAndSwapInt
public final native boolean compareAndSwapInt(Object var1, long var2,
int var4, int var5);
变量增加时的方法
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;
}
可以看到增加时若得到的值和预期值不同,便一直循环。
二、CAS的缺点
既然CAS能保证线程安全,而且不加锁,并发效率高,那么,它是不是就是完美的呢?世上没有完美的事物,它也不是的,那么它的缺点是什么?有如下缺点:
CAS缺点:
循环时间长,开销大
只能保证一个共享变量的操作
三、ABA问题?
既然CAS能保证线程安全,而且不加锁,并发效率高,那么,它是不是就是完美的呢?世上没有完美的事物,它也不是的,那么它的缺点是什么?就是ABA问题,所谓ABA问题,举个例子,就是当A线程在主内存中拿到100并修改为101后写回主内存,然后同样再将101修改为100时,B线程启动,在主内存中拿到这个100,但是B此刻发现值仍为100,则认为该100的值没有被修改过,而其实不然,100已经被A修改过。(还修改了2次!!!)这就是所谓的ABA问题。
四、如何解决ABA问题?
修改版本号,用AtomicStampedReference,运用该类的compareAndSet方法可以定义版本号,从而在每次修改时,设置版本号也修改,这样便可以通过版本号来辨别该类是否已经被修改,以下是该类的底层源码
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
private static class Pair<T> {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
有什么问题,欢迎指出!!!