1074 字
5 分钟
Java---CAS与原子类
2025-03-10

CAS#

介绍#

CAS(Compare-And-Swap,比较并交换)是一种 无锁并发编程的核心机制,用于实现多线程环境下的原子操作。它通过 硬件级别的原子指令 直接支持,避免了传统锁(如 synchronized)带来的线程阻塞和上下文切换开销,是高性能并发编程的基石。

核心原理#

CAS 操作包含三个关键参数:

  • 内存地址(V):需要更新的变量地址。
  • 旧的预期值(A):操作前读取到的变量值。
  • 新值(B):希望将变量更新为的值。

操作逻辑

  1. 读取内存地址 V 的当前值。
  2. 比较当前值是否等于预期值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)
3CAS 成功i=6CAS 失败(因值已改变)线程 B 需重试,直到成功

通过 自旋重试(循环尝试 CAS),最终保证所有线程的修改被正确应用。

优点#

  1. 无锁并发: 无需阻塞线程,减少上下文切换开销,适合高并发场景。
  2. 轻量高效: CAS 是硬件级别的原子操作,性能远高于传统锁(如 synchronized)。
  3. 避免死锁: 无锁机制天然避免了死锁问题。

缺点#

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
需要无锁高性能并发队列(如 ConcurrentLinkedQueueCAS + 自旋
需避免死锁实时系统CAS 替代锁

总结#

CAS 是一种基于硬件支持的 无锁并发机制,通过 比较并交换 实现原子操作,避免了传统锁的性能开销。尽管存在 ABA 问题和自旋限制,但通过版本号、分散竞争等优化手段,它仍是构建高性能并发系统的核心工具。在 Java 中,AtomicXXX 类和 Unsafe 的封装让 CAS 的使用更加便捷和安全。

原子类#

分类#

类名用途示例方法
AtomicInteger原子操作 int 类型incrementAndGet()
AtomicLong原子操作 long 类型getAndAdd()
AtomicReference原子操作对象引用compareAndSet()
AtomicStampedReference解决 ABA 问题(带版本号)compareAndSet()
LongAdder高并发下替代 AtomicLongadd()sum()

性能对比#

场景synchronizedAtomicIntegerLongAdder
低并发(少量线程)中等中等
高并发(大量线程)低(阻塞严重)中等(自旋开销)极高

总结#

原子类通过 CAS + volatile + Unsafe 实现无锁线程安全,核心是 硬件级原子指令。适合替代 synchronized 实现轻量级并发,但在高竞争场景需权衡自旋开销(可优先选择 LongAdder)。

Java---CAS与原子类
https://fuwari.vercel.app/posts/cas/
作者
Lettle
发布于
2025-03-10
许可协议
CC BY-NC-SA 4.0