什么是CAS?
Unsafe是CAS的核心类,本来java方法无法直接访问底层系统,需要通过调用Native方法才可以。但是java给自己留了一个后门,基于Unsafe类可以直接操作特定内存的数据。其内部方法可以像C的指针一样直接操作内存,且CAS操作依赖于Unsafe类中的方法。同时CAS是并发编程里,实现无锁最关键的部分之一。在JUC包中大量使用了CAS,拿最常见的AtomicInteger来看一下:
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));// getDeclaredField是可以获取一个类的所有字段.
// getField只能获取类的public 字段.
} catch (Exception ex) { throw new Error(ex); }
}
// 变量使用volatile修饰保证了多线程之间的内存可见性
private volatile int value;
我们可以看到在AtomicInteger 注释中明确指出了,使用Unsafe类的CAS进行更新操作。下面是具体使用案例
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);当前对象,内存偏移量
}
================================================================================
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;
}
除了compareAndSwapInt外还有compareAndSwapLong和compareAndSwapObject来对应操作不同数据类型
- var1:要操作的对象
- var2:要操作对象中属性地址的偏移量(因为我们只能获取到对象地址,如果想拿到对象中属性地址必须根据偏移量来获取)
- var3:要修改数据的期望值
- var4:表示需要修改为的新值
CAS原理: 它是一条CPU并发原语,调用Unsafe中的CAS方法,JVM会帮我们实现出CAS的汇编指令(通过C语言调用底层CPU指令)。这是一种完全依赖于硬件的功能,通过它实现了原子操作。由于CAS是一种系统原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某一个功能的过程。且原语的执行是连续不可被中断的,所以不会造成数据不一致问题。
不同则继续取值比较,直至相同(自旋锁思想)
CAS缺点:
1.循环时间长性能开销大
2.只能保证一个共享变量的原子操作
3.ABA问题
ABA问题:
不要想的太难,就按字面理解就可以。在某一阶段A变为了B,但是最后又变回了A。系统判断为A一直为A没有改动,但是再系统不知情的一段时间内A是有过变化的。
解决ABA:
AtomicStampedReference