1074 字
5 分钟
Java---CAS与原子类
CAS
介绍
CAS(Compare-And-Swap,比较并交换)是一种 无锁并发编程的核心机制,用于实现多线程环境下的原子操作。它通过 硬件级别的原子指令 直接支持,避免了传统锁(如 synchronized
)带来的线程阻塞和上下文切换开销,是高性能并发编程的基石。
核心原理
CAS 操作包含三个关键参数:
- 内存地址(V):需要更新的变量地址。
- 旧的预期值(A):操作前读取到的变量值。
- 新值(B):希望将变量更新为的值。
操作逻辑:
- 读取内存地址
V
的当前值。 - 比较当前值是否等于预期值
A
:- 如果相等,将内存值修改为
B
,返回成功。 - 如果不相等,返回失败(说明其他线程已修改过该值)。
- 如果相等,将内存值修改为
整个过程是 原子性 的,由 CPU 指令(如 x86 的 CMPXCHG
)直接保证,无需加锁。
工作流程
假设线程 A 和线程 B 同时对 AtomicInteger i
执行 i++
:
步骤 | 线程 A | 线程 B | 结果 |
---|---|---|---|
1 | 读取 i=5 (预期值 A=5) | 读取 i=5 (预期值 A=5) | |
2 | 尝试将 i 更新为 6 (B=6) | 尝试将 i 更新为 6 (B=6) | |
3 | CAS 成功,i=6 | CAS 失败(因值已改变) | 线程 B 需重试,直到成功 |
通过 自旋重试(循环尝试 CAS),最终保证所有线程的修改被正确应用。
优点
- 无锁并发: 无需阻塞线程,减少上下文切换开销,适合高并发场景。
- 轻量高效: CAS 是硬件级别的原子操作,性能远高于传统锁(如
synchronized
)。 - 避免死锁: 无锁机制天然避免了死锁问题。
缺点
ABA 问题
- 问题描述: 若变量值从
A
变为B
再变回A
,CAS 会误认为未被修改。例如: 线程 1 读取i=5
,线程 2 修改i=6
后又改回i=5
,线程 1 的 CAS 仍会成功。 - 解决方案: 使用 版本号 或 时间戳 标记变量状态。 Java 中的
AtomicStampedReference
即通过版本号解决 ABA 问题。
自旋开销
- 高并发场景下,若线程频繁 CAS 失败,会导致 CPU 空转,浪费资源。
- 优化方案: 限制自旋次数(如
LongAdder
分散竞争)或退化为锁机制。
单一变量限制
- CAS 只能保证 单个变量 的原子性,无法直接支持复合操作(如
i++
和j++
的原子组合)。
Java中CAS实现
(1)Unsafe
类
Java 通过 sun.misc.Unsafe
类提供 CAS 的底层操作。例如:
public final native boolean compareAndSwapInt(
Object o, long offset, int expected, int newValue
);
offset
:变量在对象中的内存偏移量。expected
:旧的预期值。newValue
:新值。
(2)原子类(AtomicInteger 示例)
AtomicInteger atomicInt = new AtomicInteger(5);
// CAS 操作
boolean success = atomicInt.compareAndSet(5, 6);
// 成功返回 true,失败返回 false
(3)AtomicStampedReference 解决 ABA 问题
AtomicStampedReference<Integer> ref =
new AtomicStampedReference<>(5, 0); // 初始值 5,版本号 0
// 更新时检查值和版本号
boolean success = ref.compareAndSet(5, 6, 0, 1);
// 旧值=5,新值=6,旧版本号=0,新版本号=1
CAS适用场景
场景 | 示例 | 替代方案 |
---|---|---|
低竞争环境 | 计数器、状态标志 | AtomicInteger |
高并发读多写少 | 缓存系统 | LongAdder |
需要无锁高性能 | 并发队列(如 ConcurrentLinkedQueue ) | CAS + 自旋 |
需避免死锁 | 实时系统 | CAS 替代锁 |
总结
CAS 是一种基于硬件支持的 无锁并发机制,通过 比较并交换 实现原子操作,避免了传统锁的性能开销。尽管存在 ABA 问题和自旋限制,但通过版本号、分散竞争等优化手段,它仍是构建高性能并发系统的核心工具。在 Java 中,AtomicXXX
类和 Unsafe
的封装让 CAS 的使用更加便捷和安全。
原子类
分类
类名 | 用途 | 示例方法 |
---|---|---|
AtomicInteger | 原子操作 int 类型 | incrementAndGet() |
AtomicLong | 原子操作 long 类型 | getAndAdd() |
AtomicReference | 原子操作对象引用 | compareAndSet() |
AtomicStampedReference | 解决 ABA 问题(带版本号) | compareAndSet() |
LongAdder | 高并发下替代 AtomicLong | add() 、sum() |
性能对比
场景 | synchronized | AtomicInteger | LongAdder |
---|---|---|---|
低并发(少量线程) | 中等 | 高 | 中等 |
高并发(大量线程) | 低(阻塞严重) | 中等(自旋开销) | 极高 |
总结
原子类通过 CAS + volatile + Unsafe 实现无锁线程安全,核心是 硬件级原子指令。适合替代 synchronized
实现轻量级并发,但在高竞争场景需权衡自旋开销(可优先选择 LongAdder
)。