什么是CAS
CAS的全称是Compare and swap, 它是一条CPU并发原语。
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。整个过程是原子性的。
CAS并发原语体现在JAVA语言就是sun.misc.Unsafe类中的各个方法。调用Unsafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。
CAS相比于锁的优势
在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁,出现性能问题。(关于synchronized参考文章解析synchronized实现原理_我不是欧拉_的博客-CSDN博客)。
锁机制存在以下问题:
- 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。
- 一个线程持有锁会导致其它所有需要此锁的线程挂起。
- 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。
volatile是不错的机制,但是volatile不能保证原子性(关于volatile参考文章解析volatile实现原理_我不是欧拉_的博客-CSDN博客)。因此对于同步最终还是要回到锁机制上来。
独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。
CAS可以看做是乐观锁的一种实现方式,所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
sun.misc.Unsafe中的递增操作就通过CAS自旋实现的。 CAS是一种无锁算法,在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。即用户模式下的线程同步,非内核模式
CAS流程图:
CAS实现原理
通过AtomicInteger类来研究在没有锁的情况下CAS是如何做到数据正确性的。
1. 解析getAndIncrement方法
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
// 获取unsafe对象
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
// objectFieldOffset()方法用于获取某个字段相对Java对象的“起始地址”的偏移量
// 获取value字段相对于AtomicInteger对象起始位置的偏移量
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
// 借助volatile,保证线程间的数据的可见性
private volatile int value;
/**
* Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
// 构造函数初始化值
public AtomicInteger(int initialValue) {
value = initialValue;
}