CAS是什么
CAS是Java中Unsafe类里面的方法,它的全称是CompareAndSwap,比较并交换的意思。它的主要功能是能够保证在多线程环境下,对于共享变量的修改的原子性,并不是CAS单点登录。
非CAS操作
举个例子,比如说有这样一个场景,有一个成员变量flag,默认值为0,等于0就修改成1。这个逻辑看起来没有任何问题,但是在多线程环境下,会存在原子性的问题,因为这里是一个典型的,Read-Write的操作。一般情况下,我们会在updateFlag()这个方法上加同步锁来解决原子性问题。
public class Demo {
private int flag = 0;
public void updateFlag() {
//如果是0就改为1
if (flag == 0) {
flag = 1;
}
}
}
CAS操作
如果上面加同步锁,会带来性能上的损耗,所以,对于这类场景,我们就可以使用CAS机制来进行优化。
在 updateFlag()方法中,我们调用了 unsafe 类中的 compareAndSwapInt()方法来达到同样的目的。
import sun.misc.Unsafe;
public class Demo {
private volatile int flag = 0;
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final Long flagValue;
static {
try {
flagValue = unsafe.objectFieldOffset(Demo.class.getDeclaredField("flag"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void updateFlag() {
/**
* 第一个参数:当前对象实例
* 第二个参数:成员变量 flag 在内存地址中的偏移量
* 第三个参数:预期值 0
* 第四个参数:期望更改之后的值 1
*/
if (unsafe.compareAndSwapInt(this, flagValue, 0, 1)) {
//todo
}
}
}
CAS 机制会比较 flag 内存地址偏移量对应的值和传入的预期值 0 是否相等,如果相等,就直接修改内存地址中 flag 的值为 1. 否则,返回 false,表示修改失败,而这个过程是原子的,不会存在线程安全问题。
CAS总结
CompareAndSwap 是一个 native 方法,实际上它最终还是会面临同样的问题,就是先从内存地址中读取 flag 的值,然后去比较,最后再修改。这个过程不管是在什么层面上实现,都会存在原子性问题。所以CompareAndSwap 的底层实现中,在多核 CPU 环境下,会增加一个Lock 指令对缓存或者总线加锁,从而保证比较并替换这两个指令的原子性。
CAS 主要用在并发场景中,比较典型的使用场景有两个。
第一个是 J.U.C 包里面 Atomic 的原子实现,比如 AtomicInteger、AtomicLong。
第 二 个 是 实 现 多 线 程 对 共 享 资 源 竞 争 的 互 斥 性 质 , 比 如 在 AQS 、ConcurrentHashMap、ConcurrentLinkedQueue 等都有用到。