Java高频面试之并发编程-24
hello啊,各位观众姥爷们!!!本baby今天又来报道了!哈哈哈哈哈嗝🐶
面试官:什么是CAS
CAS(Compare and Swap,比较并交换) 是一种 无锁(Lock-Free) 的原子操作机制,用于实现多线程环境下对共享变量的安全更新。其核心思想是“先检查再修改”,通过硬件指令(如 CPU 的 CMPXCHG
)保证操作的原子性,避免使用传统锁机制带来的性能开销。
CAS 的核心原理
CAS 操作包含三个参数:
- 内存地址(V):需要修改的共享变量的内存地址。
- 期望值(A):该变量当前应具备的值(用于判断是否被其他线程修改过)。
- 新值(B):希望将该变量更新为的值。
执行流程:
- 比较:检查内存地址
V
中的当前值是否等于A
。 - 交换:
- 若相等,说明变量未被其他线程修改,将
V
的值更新为B
,返回true
。 - 若不相等,说明变量已被其他线程修改,放弃更新,返回
false
。
- 若相等,说明变量未被其他线程修改,将
整个过程由 硬件指令 保证原子性,不会出现中间状态。
CAS 的伪代码
function CAS(V, A, B) -> bool {if *V == A {*V = Breturn true} else {return false}
}
CAS 的典型应用场景
1. 原子类(如 AtomicInteger
)
Java 中的 AtomicInteger
使用 CAS 实现线程安全的数值操作:
AtomicInteger count = new AtomicInteger(0);// 线程安全的自增操作(底层基于 CAS)
count.incrementAndGet();
2. 无锁数据结构
- 无锁队列、无锁栈:通过 CAS 实现并发环境下的高效插入和删除。
- 自旋锁(SpinLock):线程通过 CAS 循环尝试获取锁,避免线程阻塞。
3. 并发工具
ReentrantLock
的 AQS 内部通过 CAS 管理同步队列的入队和出队。ConcurrentHashMap
在 JDK 8+ 中利用 CAS 优化桶节点的插入。
CAS 的优缺点
优点 | 缺点 |
---|---|
无锁设计,减少线程阻塞和上下文切换 | 自旋开销:竞争激烈时循环重试可能浪费 CPU 资源 |
高并发场景下性能优于传统锁 | ABA 问题:变量可能被其他线程修改后又恢复原值,导致误判 |
避免死锁风险 | 仅适用于简单操作,复杂逻辑需结合其他机制 |
CAS 的 ABA 问题
问题描述
- 线程 1 读取变量值为
A
。 - 线程 2 将变量从
A
修改为B
,再修改回A
。 - 线程 1 执行 CAS 时,误认为变量未被修改过,导致逻辑错误。
解决方案
- 版本号标记:每次修改变量时递增版本号,CAS 同时检查值和版本号。
- Java 实现:使用
AtomicStampedReference
或AtomicMarkableReference
。
AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(100, 0);// 更新时检查值 + 版本号
int stamp = ref.getStamp();
ref.compareAndSet(100, 200, stamp, stamp + 1);
CAS 的底层实现
- 硬件支持:现代 CPU 提供
CMPXCHG
指令(如 x86 架构),操作系统或 JVM 将其封装为原子操作。 - Java 中的实现:通过
sun.misc.Unsafe
类的compareAndSwapInt
、compareAndSwapLong
等本地方法调用硬件指令。
CAS 与锁的对比
特性 | CAS | 传统锁(如 synchronized ) |
---|---|---|
并发策略 | 乐观锁(假设无冲突,冲突时重试) | 悲观锁(假设有冲突,直接阻塞) |
线程阻塞 | 无(自旋等待) | 有(线程进入阻塞状态) |
适用场景 | 低竞争、简单操作(如计数器) | 高竞争、复杂临界区 |
性能 | 高并发下更高效 | 竞争激烈时上下文切换开销大 |
你想要的技术资料我全都有:https://pan.q删掉汉子uark.cn/s/aa7f2473c65b