1. 什么是CAS

CAS 即 compare and swap(比较与交换),是一种有名的无锁算法。即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)

2. 为什么会有CAS

因为操作系统的锁太重了, 太消耗性能了,CAS使用CPU的原语支持, 更快。

3.CAS原理

CAS 中涉及三个要素:

  • 需要读写的内存值 V
  • 进行比较的值 A
  • 拟写入的新值 B

当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

  1. public static int i = 100;
  2. /**
  3. * @param compare 比较值
  4. * @param expect 期望值
  5. * @param update 修改值
  6. * @return 是否修改成功
  7. */
  8. public final boolean compareAndSet(int expect, int update) {
  9. //查询expect期望值
  10. //比较 i 是否和 expect相等
  11. //如果相等则修改为update的值,返回true,
  12. //如果不相等则认为期望值被其他线程修改过,不作任何操作直接返回false.
  13. }

可能有人会问:这个方法的实现也不能保证原子性啊,多个线程也会并发执行这个方法也是线程不安全。
需要注意的是,这个方法只是一个思维逻辑, 真正的CAS实现是由CPU支持的,属于硬件天生支持。intel层面使用 exchage 指令实现。

4. CAS的优缺点

4.1 CAS的优点

  1. 性能快: 不经过操作系统, 不会放弃CPU,一直占用CPU。一旦符合要求立即更新字段。

    4.2 CAS的缺点

  2. 循环开销大,线程较多时占用CPU严重。

    我们知道乐观锁在进行写操作的时候会判断是否能够写入成功,如果写入不成功将触发等待 -> 重试机制,这种情况是一个自旋锁,简单来说就是适用于短期内获取不到,进行等待重试的锁,它不适用于长期获取不到锁的情况,另外,自旋循环对于性能开销比较大。

  3. 只能锁定单个变量,无法锁定某个对象或者语句块。

  4. ABA问题。

    4.1 ABA问题。

    ABA 问题说的是,如果一个变量第一次读取的值是 A,准备好需要对 A 进行写操作的时候,发现值还是 A,那么这种情况下,能认为 A 的值没有被改变过吗?可能是有 A->B->A 的这种情况,但是 CAS 从原理上讲,却不会这么认为,它只相信它看到的,它看到的是什么就是什么。

ABA问题的解决方式:
版本号机制:每次这个字段更新是就增加一个版本号。 只有值和版本号同时匹配时才会认为这个值可以进行cas。

  1. public static int i = 100;
  2. /**
  3. * @param compare 比较值
  4. * @param expect 期望值
  5. * @param version 版本号
  6. * @param update 修改值
  7. * @return 是否修改成功
  8. */
  9. public final boolean compareAndSet(int expect, int version, int update) {
  10. //查询expect的当前的值
  11. //比较 i 是否和 expect相等并且版本号是否也相等
  12. //如果都相等则修改为update的值,返回true,
  13. //如果有一个不相等则认为期望值被其他线程修改过,不作任何操作直接返回false.
  14. }

5. CAS的应用

java中的 java.util.concurrent.atomic.Atomic* 都是CAS算法的实现。

6. CAS 和 synchronized

简单的来说 CAS 适用于写比较少的情况下(多读场景,冲突一般较少),synchronized 适用于写比较多的情况下(多写场景,冲突一般较多)

  • 对于资源竞争较少(线程冲突较轻)的情况,使用 Synchronized 同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗 cpu 资源;而 CAS 基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。
  • 对于资源竞争严重(线程冲突严重)的情况,CAS 自旋的概率会比较大,从而浪费更多的 CPU 资源,效率低于 synchronized。