Android面试:说一下你对CAS的理解

Android面试中常会被问到Java并发编程中的一些问题,比如我们将要聊的CAS就是其中一个。

大致理解

首先我们要大致找到什么是CAS——CAS就是compare and swap,它不是一个类,也不是接口,而是一种“无锁并发”的思想,你可以理解为一种“乐观的更新策略”来实现无锁并发。同时Java 提供了具体的工具来实现它:原子类(如 AtomicInteger,AtomicReference)。

具体例子

那我们怎么理解这种“乐观的更新策略”呢?我用一个开发过程中的具体例子来让你理解。

线程A和线程B都想要对共享变量a=1进行修改:

线程A想将 a 从 1 改为 2,线程B想将 a 从 1 改为 3

CAS的详细步骤:

1. 线程A的操作
  1. 读取旧值:线程A先读取 a 的当前值,记录为 oldValue = 1

  2. 计算新值:线程A基于旧值计算新值 newValue = oldValue + 1 = 2

  3. 提交修改(CAS操作)

    • 比较:检查当前内存中的 a 是否还是 oldValue(即 1)。

    • 交换

      • 如果 a 未被修改(仍是 1),将 a 更新为 newValue = 2 → 成功。

      • 如果 a 已被修改(比如被线程B抢先改为 3),则放弃更新或者自旋重试。

2. 线程B的操作
  1. 读取旧值:线程B也读取 a 的当前值,记录为 oldValue = 1(此时线程A尚未提交修改)。

  2. 计算新值:线程B计算新值 newValue = oldValue + 2 = 3

  3. 提交修改(CAS操作)

    • 比较:检查当前内存中的 a 是否还是 1

    • 交换

      • 如果此时线程A的 CAS 尚未执行,线程B发现 a 仍为 1 → 更新为 3 → 成功。

      • 如果线程A已抢先更新为 2 → 线程B的 CAS 失败,需要自旋重试或放弃。

上述过程中讲的自旋是什么

假设线程 A 和线程 B 同时 尝试修改 a,但线程 A 的 CAS 操作先成功,线程 B 的第一次 CAS 失败,随后自旋重试。

1. 第一次尝试(失败)

  1. 读取旧值:线程 B 读取 a = 1,记录 oldValue = 1

  2. 计算新值:newValue = oldValue + 2 = 3

  3. 执行 CAS:

    • 比较:当前内存中的 a 是否仍是 1

      • 实际此时线程 A 已抢先完成 CAS,a 被改为 2

    • 结果:CAS 失败(因为 a != oldValue)。

  4. 自旋重试:线程 B 不阻塞,继续循环尝试。

2. 第二次尝试,开始自旋(可能成功或继续失败)

  1. 重新读取旧值:线程 B 再次读取 a 的当前值 oldValue = 2(线程 A 修改后的值)。

  2. 重新计算新值:newValue = oldValue + 2 = 4

  3. 执行 CAS:

    • 比较:当前内存中的 a 是否仍是 2

      • 若未被其他线程修改 → 更新为 4 → 成功。

      • 若被其他线程修改(例如线程 C 将 a 改为 5)→ 继续自旋。

自旋重试的关键点
  1. 无阻塞等待:线程 B 不会进入阻塞状态(如 BLOCKED),而是持续占用 CPU 时间片尝试 CAS。

  2. 适应性自旋:

    • 实际 JVM 会优化自旋次数(例如限制最大自旋次数,避免 CPU 空转)。

    • Java 的 AtomicInteger.compareAndSet() 内部通过 while 循环实现自旋。

  3. 最终一致性:只要没有其他线程持续抢占修改,线程 B 最终会成功。

CAS中的ABA问题

如果线程A读取 a=1,在 CAS 提交修改前,a 被其他线程改为 2,又被改回 1,线程A的 CAS 会误认为 a 未被修改。此时我们需要通过添加版本号来解决这个问题。

与传统加锁的对比

同样还是用上述对共享变量a=1的修改为例:

  1. 线程A修改:系统加锁 → 读取共享变量a=1 → 对a修改 a=2 并写入新值→ 释放锁,允许其他线程竞争锁。

  2. 线程B修改:等待锁 发现锁已被占用 → 阻塞 → 线程A释放锁后,线程B获取锁后 → 读取 a=2(线程A修改后的值) → 计算新值a = 3 并写入新值 → 释放锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值