一. CAS与自旋锁的关系澄清
首先要明确,CAS(Compare And Swap)是硬件提供的原子操作 ,而自旋锁是基于CAS实现的锁机制。
关系如下:
- CAS:CPU级别的原子操作指令,用于实现无锁并发控制。就像是乐观锁。
- 自旋锁:利用CAS机制实现的一种锁策略,线程在获取锁失败时不会阻塞,而是通过循环(自旋)不断尝试,直到成功。
二. CAS的底层原理
CAS操作包含三个核心参数:
- 内存值(V):当前共享变量的实际值。
- 预估值(A):线程认为该变量应有的旧值。
- 更新值(B):线程希望更新的目标值。
执行逻辑
kotlin
if V == A:
V = B
return true
else:
return false
通过一条CPU指令(如x86的CMPXCHG)保证操作的原子性,无需传统锁机制。
三. 自旋锁的实现与特性
自旋锁通过CAS+循环重试实现,典型代码结构:
java
import java.util.concurrent.atomic.AtomicReference;
public class TestCas {
private AtomicReference<Thread> owner = new AtomicReference<>();
public void lock() {
Thread current = Thread.currentThread();
while (!owner.compareAndSet(null, current)) {
// 循环自旋(空转或执行轻量级操作)
}
}
public void unlock() {
owner.compareAndSet(Thread.currentThread(), null);
}
}
特点:
- 无阻塞:线程不进入阻塞状态,避免上下文切换开开销。
- 轻量级:仅依赖CAS原子指令,不涉及操作系统调度。
四. CAS(自旋锁)的优缺点对比
维度 | 优点 | 缺点 |
---|---|---|
性能 | 无锁机制减少线程切换开销,高并发场景下吞吐量显著优于传统锁 | 高竞争时自旋循环导致CPU空转,可能浪费资源 |
适用场景 | 适合短临界区、低竞争场景(如计数器、标志位更新) | 不适合长时间持有锁或高竞争场景(如数据库事务) |
内存开销 | 仅需少量内存存储状态(如AtomicReference),无额外锁结构 | 需要处理ABA问题(需额外引入版本号或时间戳) |
复杂度 | 代码实现简单(如原子类直接封装成CAS) | 需处理重试逻辑,可能引发活锁或饥饿问题 |
扩展性 | 多核CPU下扩展性良好,CAS指令可并行执行 | 缓存一致性(如MESI)可能导致总线风暴,降低性能 |
五. 典型应用场景
1. 原子类操作
Java的AtomicInteger、AtomicLong等基于CAS实现原子增减,如:
java
import java.util.concurrent.atomic.AtomicInteger;
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet(); // CAS实现无锁递增
2. 轻量级同步工具
- ReentrantLock的轻量级锁优化(JDK内部实现)
- ConcurrentHashMap的分段锁机制,结合CAS减少锁竞争
3. 无锁数据结构
如无锁队列(Lock-Free Queue)、无锁栈(Lock-Free Stack)等,通过CAS保证线程安全:
java
// 无锁队列的入队操作
public void enqueue(Node newNode) {
Node oldTail;
do {
oldTail = tail.get();
}while (!tail.compareAndSet(oldTail, newNode));
}
4. 数据库索引并发控制
数据库管理系统(如Mysql)使用CAS实现行级锁,优化高并发写入性能。
六. 关键技术问题域解决方案
1. ABA问题
- 现象:变量值从A->B->A,CAS无法感知中间变化
- 解决方案
- 版本号机制:AtomicStampedReference添加版本标记
- 时间戳:在数据中记录时间戳(如数据库MVCC)
2.自旋时间过长
优化策略:
- 适应性自旋 :JVM动态调整自旋次数(如synchronized的轻量级锁优化)
- 退化为阻塞 :自旋超过阈值后挂起线程(如ReentrantLock的公平锁策略)
3. 多核缓存一致性
MESI协议开销:CAS操作可能导致缓存行频繁失效。
优化方案:
- 内存对齐减少伪共享(@Contended注解)
- 局部变量缓存(如线程本地存储)