Java CAS
CAS 的全称为 Compare-And-Swap(对比交换)。是一条 CPU 的原子指令,其作用是让 CPU 先进行比较两个值是否相等,然后原子地更新某个位置的值。其实现方式是基于硬件平台的汇编指令,就是说 CAS 是靠硬件实现的,JVM 只是封装了汇编调用,那些以 Atomic 开头的类便是使用了这些封装后的接口。
CAS 操作包含三个操作数 — 内存值、预期值和新值。执行 CAS 操作的时候,将内存值与预期值比较,只有在两者相等时才会对内存中的值进行修改,否则不做任何操作。
CAS 是在保证性能的同时提供并发场景下的线程安全性。在 Java 中 CAS 实现位于 sun.misc.Unsafe 类中,CAS 常用的有以下几个方法:
1 | public final int getAndSetInt(Object o, long offset, int newValue) { |
- obj: 目标操作对象
- offset: 目标操作数内存偏移地址
- expectedValue: 期望值
- newValue: 更新值
自旋锁是利用 CAS 而设计的一种应用层面的锁。如下代码:
1 | // 0代表锁释放,1代表锁被占用 |
实现线程安全的方式可以分为三类:
- 互斥同步: synchronized 和 ReentrantLock;
- 非阻塞同步: CAS、AtomicInteger 等原子类;
- 无同步方案: 栈封闭、Thread Local、只读变量;
CAS 的问题
CAS 方式为乐观锁,synchronized 为悲观锁。使用 CAS 解决并发问题通常情况下性能更优。但也存在如下几个问题:
1. ABA 问题:
因为 CAS 需要在操作值的时候,检查值有没有发生变化,没有发生变化则更新,但是如果一个值原来是 A,变成了 B,又变成了 A,那么使用 CAS 进行检查时则会发现它的值没有发生变化,但是实际上却变化了。
ABA 问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么 A-B-A 就会变成 1A - 2B-3A。
也可以使用 JDK 的 atomic 包里提供的 AtomicStampedReference 来解决。
通过检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
2. 循环时间长开销大:
3. 只能保证一个共享变量的原子操作:
JDK 提供了 AtomicReference 类来保证引用对象之间的原子性,就可以把多个变量放在同一个对象里来进行 CAS 操作。
UnSafe 类

参考
[1] Unsafe - openjdk
[2] JUC 原子类: CAS, Unsafe 和原子类详解
[3] Java 魔法类:Unsafe 应用解析