Compare-And-Swap 比较并且交换、是一条CPU并发原语.
功能:判断内存中某个位置的值与预期值是否相等,如果是则更新为新值,这个过程是原子的。
底层原理:
UnSafe类:是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地〈native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据。
Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中CAS操作的执行依赖于Unsafe类的方法。
注意Unsafe类中的所有方法都是native修饰的,也就是说Unsafe类中的方法都直接用操作系统底层资源执行相应任务。
变量valueOffset,表示该变量在内存中的偏移地址,因为UnSafe类是根据偏移地址获得数据。
变量value由volatite修饰,保证了多线程间的内存可见性。
CAS方法JVM会执行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;
}
对象原子引用
public final Object getAndSetObject(Object var1, long var2, Object var4) {
Object var5;
do {
var5 = this.getObjectVolatile(var1, var2);
} while(!this.compareAndSwapObject(var1, var2, var5, var4));
return var5;
}
缺点:
CPU开销大:CAS 里面是一个循环判断的过程,如果线程一直没有获取到状态,cpu资源会一直被占用。
只是一个共享变量的原子性操作,CAS 机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证 3 个变量共同进行原子性的更新,就得使用 synchronized 了。
CAS 容易造成 ABA 问题,一个线程 a 将数值改成了 b,接着又改成了 a,此时 CAS 认为是没有变化,其实是已经变化过了。解决方案可以使用版本号标识,每操作一次version 加 1。AtomicStampedReference 来解决