compareAndSet----比较并交换
//AtomicInteger.conpareAndSet(int expect, indt update)
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
第一个参数expect为期望值,如果期望值跟内存值还是一致,进行update赋值,如果期望值不一致,证明数据被修改过,返回fasle,取消赋值。
Unsafe
是CAS核心类,由于Java方法无法直接访问地层系统,需要通过本地(native)方法来访问。Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中CAS操作的执行依赖于Unsafe类的方法。
- Unsafe类中的所有方法都是native修饰的,方法都直接调用操作系统底层资源执行相应任务
- 变量valueOffset,表示该变量值在内存中的偏移地址,因为Unsafe就是根据内存便宜地址获取数据的
- 变量value用volatile修饰,保证多线程之间的可见性
CAS
CAS全称呼Compare-And-Swap,它是一条CPU并发原语。
由于CAS是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成数据不一致问题。
CAS缺点
//unsafe.getAndAddInt
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;
}
-
循环时间长,开销大
do...while,如果CAS失败,一直获取失败,如果CAS长时间不成功,可能会给CPU带来很大的开销。 -
只能保证一个共享变量的原子操作
对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性。
CAS的ABA问题?
比如线程1从内存取出A,线程2同时也从内存取出A,并且线程2进行一些操作将值改为B,然后线程2又将V位置数据改成A,这时候线程1进行CAS操作发现内存中的值依然时A,然后线程1操作成功,尽管线程1的CAS操作成功,但是不代表这个过程没有问题。这就产生ABA问题。
解决ABA问题?
- 原子引用、AtomicReference
- 时间戳的原子引用、AtomicStampedReference(新增机制,修改版本号)