什么是CAS

CASCompare and Swap, 其是 JDK 提供的非阻塞原子性操作, 它通过硬件保证了比较—更新操作的原子性。(volatile 只能保证共享变量的可见性,不能解决读—改—写等的原子性问题。)

JDK 里面的 Unsafe 类提供了一系列的 compareAndSwap 方法, 下面以 compareAndSwapLong *方法为例进行简单介绍。

boolean compareAndSwapLong(Object obj,long valueOffset,long expect,long update)
方法:其中 compareAndSwap 的意思是比较并交换。CAS 有四个操作数, 分别为:对象内存位置、对象中的变量的偏移量、变量预期值和新的值。其操作含义是,如果对象 obj 中内存偏移量为 valueOffset 的变量值为 expect, 则使用新的值 update 替换旧的值 expect。这是处理器提供的一个原子性指令。

CAS 实现原子操作的三大问题

1. ABA 问题

因为 CAS 需要在操作值得时候,检查值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是 A,变成了 B,又变成了 A,那么使用 CAS 进行检查时会发现它的值没有发生变化,但是实际上却变化了。

ABA 问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加 1,那么A-B-A 就会变成 1A-2B-3A。

2. 循环时间长开销大

自旋 CAS 如果长时间不成功,会给 CPU 带来非常大的执行开销。

3. 只能保证一个共享变量的原子操作

当对一个共享变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操作,但是对多个共享变量操作时,循环 CAS 就无法保证操作的原子性,这个时候就可以用锁。
还有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如,有两个共享变量 i = 2, j = a, 合并一下 ij = 2a, 然后用 CAS 来操作 ij。从 JDK 1.5 开始,JDK 提供了 AtomicReference 类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里来进行 CAS 操作。