跳到主要内容

CAS 与 ABA 问题

CAS

  • CAS 全称 Compare-And-Swap,是一条 CPU 并发原语,意为比较并交换,是一种很重要的同步思想,如果主内存的值和期望值一样,那么则进行修改,否则一直重试,直到一致为止
    原语的执行必须是连续的,在过程中不允许被中断,也就是说 CAS 是一条 CPU 的原子指令,不会造成所谓的数据不一致性问题
    CAS 的功能就是判断内存某个位置的值是否为预期值,如果是则修改为新的值,这个过程是原子性的
  • 缺点
    CAS 实际上是一种自旋锁,适用于并发量不高、线程竞争较少的情况,但是一旦线程冲突严重,循环时间太长,会给 CPU 带来很大的开销
    1. 一直循环,开销比较大
    2. 只能保证一个变量的原子操作,多个变量依然要加锁
    3. 引出了 ABA 问题

ABA 问题

  • CAS 的循环过程存在一个时间差,而这个时间差有可能带来意想不到的问题
    例子
    有两个线程 A、B

    1. 一开始都从主内存中拷贝了原值:3
    2. A 线程执行到 var5 = this.getIntVolatile,即 var5 = 3,此时 A 线程挂起
    3. 线程 B 修改原值为 4,B 线程执行完毕
    4. B 线程觉得修改错了,重新修改值为 3
    5. A 线程被唤醒,执行 this.compareAndSwapInt(),发现这个时候主内存的值等于快照值 3(但不知道 B 线程修改过值),修改成功

    尽管线程 A CAS 操作成功,但不代表就没有问题,有的需求只注重头和尾(比如 CAS),只要首尾一致就接受,但是有的需求还注重过程,中间不能发生任何修改

  • AtomicReference 原子引用
    AtomicInteger 对整数进行原子操作,而 AtomicReference 用于包装 POJO,使其操作原子化,本质是比较两个对象的地址是否相等

  • AtomicStampsReference 和 ABA 问题的解决
    AtomicStampsReference 类维护了一个版本号(stamp),类似于乐观锁的意思
    在进行 CAS 操作时不仅要比较当前值,还要比较版本号,只有两者都相等,才进行更新操作