一、CAS 简述
CAS:比较并交换(CompareAndSwap),应用于多线程并发场景中,实现了无锁状态下的线程安全。CAS 避免了使用互斥锁,当多个线程同时进行 CAS 指令更新同一个变量时,只有其中一个线程能够操作成功,而其他线程都会更新失败。但此时更新失败的线程并不会被阻塞,而是还可以再次 CAS 尝试。
二、Unsafe 类
sun.misc.Unsafe 是 Java 提供的一个可直接操作内存空间的类。其特点:
1)、不受 JVM 管理,使用 Unsafe 操作内存无法被 JVM GC,需要手动 GC,此时容易出现内存泄漏。
2)、Unsafe 的很多方法必须提供原始地址(内存地址)和 被替换对象的地址,偏移量要自己计算(其提供的有计算偏移量的方法),如果出现问题将会导致整个JVM 实例崩溃。
3)、直接操作内存,所以速度更快,在高并发的条件之下能够很好地提高效率。
Unsafe 类中有大量的被 native 关键字修饰的方法,这些方法是由 C/C++ 实现,底层调的是 C/C++ 的库函数,还有一些基于 native 方法封装的其他方法,整个Unsafe中的方法大致可以归结为以下几类:初始化操作、操作对象属性、操作数组元素、线程挂起和恢复和 CAS 机制。
Unsafe 类中的 compareAndSwap方法:
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
三、CAS 原理
CAS 有三个操作数:内存值 V、预期值 A、要修改的值 B。仅当预期值 A 和当前的内存值 V 相同时,才将内存值修改为 B ,否则什么都不做。
1)、Unsafe 类中的所在方法都是native修饰的,也就是送 Unsafe 类中的方法都直接调用操作系统底层资源执行相应任务,故可以直接比较内存值和期望值。value 值被 volatile 修饰后保证了其可见性,一旦发生偏移就会通知本地内存。
2)、变量 valueOffset ,表示该变量值在内存中的偏移地址,因为 Unsafe 就是根据内存偏移地址获取数据的。
3)、变量value 用 volatile修饰,保证了多线程之间内存的可见性
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
/*
var1 Atomiclntegler对象本身。
var2该对象值得引用地址。
var4 需要变动的数量。
var5是用过var1 var2找出的主内存中真实的值。
用该对象当前的值与var5比较:
如果相同,更新var5+var4并且返回true,如果不同,继续取值然后再比较,直到更新完成。
*/
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
compareAndSwapInt 的 JNI 本地实现代码:
CAS 缺点:
1)、循环时间长开销很大,unsafe 底层如果不匹配成功会一直 do-while 下去(如下图代码),一直不成功会造成很大的 cpu 开销)
2)、只能保证一个共享变量的原子操作。(一次只能保证一个对象)
3)、可能产生ABA问题