跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

CAS 原理与 Java 原子类实战

CAS 是一种无锁并发控制技术,基于 CPU 原子指令实现比较并交换。其核心包含内存位置、预期原值和目标新值,默认假设无冲突,失败则重试。主要问题包括 ABA 问题(可通过版本号解决)、自旋开销(高冲突下性能下降)及单变量限制。Java 原子类(如 AtomicInteger、LongAdder)封装了 Unsafe 操作,提供基本类型、数组、引用及字段更新支持。相比悲观锁,CAS 在低竞争下性能更优,适合计数器及无锁数据结构;高竞争时 LongAdder 通过分段优化优于 AtomicLong。

霸天发布于 2026/2/26更新于 2026/9/1274 浏览
CAS 原理与 Java 原子类实战

引言

在高并发编程领域,线程安全是永恒的核心议题。传统的悲观锁机制(如 synchronized、ReentrantLock)通过阻塞线程实现资源互斥,虽能保证安全性,但会带来上下文切换、线程唤醒等性能开销。而 CAS(Compare-And-Swap,比较并交换)作为乐观锁的核心实现,以无锁方式实现原子操作,成为高并发场景下提升性能的关键技术。

一、什么是 CAS

  • CAS(Compare-And-Swap):即比较并交换,是一种无锁的并发控制技术
  • 包含三个操作数
    • 内存位置(V):需要更新的共享变量在内存中的地址
    • 预期原值(A):线程读取到的变量当前值,即期望变量保持的值
    • 目标新值(B):变量需要更新到的最终值

CAS 的操作流程可概括为:读取内存位置 V 的当前值,若该值与预期原值 A 一致,则将 V 更新为新值 B 并返回成功;若不一致,则说明变量已被其他线程修改,放弃本次更新并返回失败。

// 伪代码表示 CAS 操作
boolean compareAndSwap(MemoryAddress V, ExpectedValue A, NewValue B) {
    if (V == A) {
        V = B;
        return true;
    }
    return false;
}

其核心思想是'乐观假设无冲突'——默认不会有其他线程同时修改变量,仅在冲突发生时通过重试(而非阻塞)规避问题,从而避免了锁机制的性能损耗。

二、CAS 的底层实现

CAS 的原子性无法通过纯软件实现,必须依赖硬件层面的原子指令支持,整个依赖链路分为三层。

1、CPU 原子指令支撑

  • 不同架构的 CPU 提供了专门的原子指令来实现 CAS 操作:
    • x86 架构:提供 cmpxchg(Compare and Exchange)指令,该指令可在一条指令内完成'比较 - 交换'的原子操作
    • ARM 架构:通过 ldrex(加载并标记)和 strex(存储并检查标记)指令对实现,标记期间其他线程无法修改目标内存地址,确保操作原子性
  • 这些指令保证了操作的原子性,即使在多核处理器环境下也不会被中断。

2、Unsafe 类:Java 的底层'魔法工具'

Java 并未直接暴露 CPU 指令,而是通过 sun.misc.Unsafe 类提供的 native 方法封装底层操作。Unsafe 类是 JVM 内部使用的'危险类',允许直接操作内存地址和调用底层指令,其提供的 CAS 相关方法是原子类的核心依赖:

public final class Unsafe {
    // 比较并交换 int 值
    public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
    // 比较并交换 long 值
    public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);
    // 比较并交换 Object 值
    public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x);
}

这些方法通过 native 关键字调用 C++ 内联汇编代码,最终触发对应 CPU 架构的原子指令。需要注意的是,Unsafe 类不建议普通开发者直接使用,其操作无安全校验,可能引发内存越界等严重问题。

3、volatile 关键字:保证内存可见性

  • CAS 操作依赖变量的实时可见性,否则线程可能读取到过期值。原子类中的共享变量均通过 volatile 修饰
    • 可见性:一个线程对变量的修改会立即刷新到主内存,其他线程读取时直接从主内存获取,避免缓存一致性问题
    • 禁止指令重排:确保变量操作的顺序与代码逻辑一致,避免 CAS 操作被重排导致的逻辑错乱

三、CAS 的核心问题与解决方案

1、ABA 问题:值的'伪不变'陷阱

ABA 问题是 CAS 最经典的逻辑漏洞:变量从 A 被修改为 B,随后又被改回 A,CAS 操作会认为'值未变化'而成功执行,但中间的状态变更可能导致依赖变量状态的逻辑出错。

  • (链表操作中的 ABA 风险) 假设单向链表初始状态为 A→B,两个线程同时执行'删除头节点'操作
    • Thread1 读取头节点为 A、下一个节点为 B,准备执行 CAS 更新头节点为 B,但被阻塞
    • Thread2 成功删除头节点 A,链表变为 B;随后新增节点 A,链表变为 A→B(新 A 节点)
    • Thread1 恢复运行,CAS 检查头节点仍为 A,执行更新将头节点设为 B,错误删除新 A 节点的下一个节点 B,导致链表结构损坏

解决方案:不仅校验变量值,还校验其'版本',确保值的变化轨迹可追溯。Java 中提供两类原子类实现该方案。

  • AtomicStampedReference(版本戳记引用):为对象引用绑定一个 int 类型的版本号(stamp),每次修改时同时更新值和版本号(版本号递增)。CAS 操作需同时匹配'值'和'版本号'才会成功,即使值回滚,版本号也会变化,从而避免 ABA 问题
// 初始化:值为"A",版本号为 1
AtomicStampedReference<String> asr = new AtomicStampedReference<>("A", 1);
// 获取当前值和版本号
int oldStamp = asr.getStamp();
String oldValue = asr.getReference(); // "A"
// 仅当'值为 A 且版本号为 1'时,更新为"B"并将版本号设为 2
boolean success = asr.compareAndSet(oldValue, "B", oldStamp, oldStamp + 1);

2、自旋开销问题:高冲突下的性能损耗

CAS 失败时会通过'自旋'(循环重试 CAS 操作),若并发冲突剧烈,大量线程会持续自旋,占用 CPU 资源,导致性能下降。

解决方案:

  • 自适应自旋:JVM 可根据历史自旋成功率动态调整自旋次数,成功率高则增加次数,反之减少
  • 分段锁优化:如 LongAdder(后续原子类部分详解),将变量拆分为多个分段,线程仅操作对应分段,降低冲突概率
  • 冲突阈值控制:当自旋次数超过阈值时,降级为阻塞锁,避免 CPU 空耗

自旋锁:线程拿不到锁就循环重试,无上下文切换开销,但消耗 CPU。适合短耗时操作 阻塞锁:线程拿不到锁就休眠,不消耗 CPU,但有巨大的上下文切换开销。适合长耗时操作

3、只能保证单个变量的原子操作

CAS 仅能对单个变量实现原子操作,无法直接保证多个变量组合操作的原子性(如'更新变量 a 同时更新变量 b')。

解决方案:

  • 将多个变量封装为一个对象,通过 AtomicReference 原子更新对象引用,间接实现组合操作的原子性
  • 必要时使用锁机制(如 ReentrantLock)包裹多个 CAS 操作,牺牲部分性能换取组合原子性

四、Java 原子类:CAS 的上层封装与实战

Java 并发包(java.util.concurrent.atomic)提供了一系列原子类,基于 sun.misc.Unsafe 类封装了常用原子操作,无需开发者直接操作底层指令。根据操作目标类型,原子类可分为四大类。

1、基本类型原子类

  • 针对 int、long、boolean 三种基本类型,提供对应的原子类,核心方法包括自增、自减、CAS 更新等
    • AtomicInteger:原子更新 int 类型值
    • AtomicLong:原子更新 long 类型值
    • AtomicBoolean:原子更新 boolean 类型值(底层通过 int 类型转换实现,0 表示 false,1 表示 true)

AtomicInteger - 原子更新 int 类型

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerExample {
    public static void main(String[] args) {
        // 创建 AtomicInteger 实例,初始值为 0
        AtomicInteger atomicInt = new AtomicInteger(0);
        // 基本操作示例
        System.out.println("初始值:" + atomicInt.get()); // 输出:0
        // 设置新值
        atomicInt.set(10);
        System.out.println("设置后:" + atomicInt.get()); // 输出:10
        // 原子递增
        atomicInt.incrementAndGet(); // i++
        System.out.println("incrementAndGet 后:" + atomicInt.get()); // 输出:11
        atomicInt.getAndIncrement(); // ++i
        System.out.println("getAndIncrement 后:" + atomicInt.get()); // 输出:12
        // 原子递减
        atomicInt.decrementAndGet(); // i--
        System.out.println("decrementAndGet 后:" + atomicInt.get()); // 输出:11
        atomicInt.getAndDecrement(); // --i
        System.out.println("getAndDecrement 后:" + atomicInt.get()); // 输出:10
        // 原子加法
        atomicInt.addAndGet(5); // 加 5 并返回新值
        System.out.println("addAndGet(5) 后:" + atomicInt.get()); // 输出:15
        int oldValue = atomicInt.getAndAdd(3); // 加 3 但返回旧值
        System.out.println("getAndAdd(3) - 旧值:" + oldValue + ", 当前值:" + atomicInt.get()); // 输出:旧值:15, 当前值:18
        // 原子更新(CAS 操作)
        boolean success = atomicInt.compareAndSet(18, 20); // 如果当前值是 18,则更新为 20
        System.out.println("CAS 操作成功:" + success + ", 当前值:" + atomicInt.get()); // 输出:true, 20
        // 获取并更新
        int newValue = atomicInt.updateAndGet(x -> x * 2); // 对当前值进行运算并返回新值
        System.out.println("updateAndGet(x->x*2) 结果:" + newValue); // 输出:40
    }
}

核心源码解析(以 AtomicInteger 为例)

public class AtomicInteger {
    // 获取 Unsafe 实例
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    // value 字段的内存偏移量(用于直接定位内存地址)
    private static final long valueOffset;
    // 共享变量,volatile 保证可见性
    private volatile int value;

    static {
        try {
            // 计算 value 字段在类中的内存偏移量
            valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    // CAS 核心方法:预期值与当前值一致则更新
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    // 原子自增(返回旧值)
    public final int getAndIncrement() {
        // 自旋 CAS:失败则重新读取并重试
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    // 原子自增(返回新值)
    public final int incrementAndGet() {
        return getAndAddInt(1) + 1;
    }
}

其中,Unsafe.getAndAddInt 方法底层通过 do-while 循环实现自旋

public final class Unsafe {
    // 以 volatile 语义读取对象 o 中偏移量为 offset 的 int 类型字段的值,并返回这个 int 值
    public native int getIntVolatile(Object o, long offset);
    // int 类型原子操作比较并交换
    public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);

    public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            // 以 volatile 方式读取当前值(保证可见性)
            v = getIntVolatile(o, offset);
        } while (!compareAndSwapInt(o, offset, v, v + delta));
        // 失败则重试
        return v;
    }
}

compareAndSwapInt 工作原理(伪代码)

boolean compareAndSwapInt(Object o, long offset, int expected, int x) {
    // 1. 计算字段的实际内存地址
    int* address = (int*)((char*)o + offset);
    // 2. 读取当前值
    int current = *address;
    // 3. 比较当前值与期望值
    if (current == expected) {
        // 4. 如果相等,则写入新值(原子操作)
        *address = x;
        return true; // 成功
    } else {
        return false; // 失败
    }
}

⚠️ 注意:上面的伪代码不是原子的!真正的 compareAndSwapInt 是由 CPU 在硬件层面保证整个操作(比较 + 交换)是原子的,不会被线程调度打断

2、数组类型原子类

  • 针对数组元素的原子操作,提供三类原子类,支持通过索引原子更新数组中的元素,底层同样依赖 CAS 机制
    • AtomicIntegerArray:原子更新 int 数组元素
    • AtomicLongArray:原子更新 long 数组元素
    • AtomicReferenceArray:原子更新引用类型数组元素
// 初始化 int 数组,所有元素原子更新
AtomicIntegerArray array = new AtomicIntegerArray(new int[]{1, 2, 3});
// 原子更新索引 0 的元素为 5(仅当当前值为 1 时)
array.compareAndSet(0, 1, 5);
// 原子自增索引 1 的元素
array.incrementAndGet(1);
System.out.println(array.get(0)); // 5
System.out.println(array.get(1)); // 3

3、对象引用类型原子类

针对对象引用的原子操作,除了前文解决 ABA 问题的 AtomicStampedReference(比较引用值 + 版本戳),还包括基础的 AtomicReference(比较引用值)和 AtomicMarkableReference(比较引用值 + 标记位),用于原子更新对象引用本身。

  • AtomicReference:基本的引用原子更新
class Data {
    private String content;
    // 构造器、getter、setter省略
}

public class AtomicReferenceDemo {
    private static final AtomicReference<Data> cacheRef = new AtomicReference<>(new Data("初始缓存"));

    // 原子更新缓存
    public static void updateCache(Data newData) {
        cacheRef.compareAndSet(cacheRef.get(), newData);
    }

    public static void main(String[] args) {
        Data newCache = new Data("更新后缓存");
        updateCache(newCache);
        System.out.println(cacheRef.get().getContent()); // 更新后缓存
    }
}
  • AtomicMarkableReference:为对象引用绑定一个 boolean 类型的标记(mark),仅表示'值是否被修改过',不记录修改次数
// 初始化:值为"A",标记为 false(未修改)
AtomicMarkableReference<String> amr = new AtomicMarkableReference<>("A", false);
boolean[] markHolder = new boolean[1]; // 用于接收当前标记
String oldValue = amr.get(markHolder); // 获取值,markHolder[0] 为 false
// 仅当'值为 A 且标记为 false'时,更新为"B"并将标记设为 true
boolean success = amr.compareAndSet(oldValue, "B", false, true);

4、对象字段更新原子类

  • 针对对象的指定字段(非静态字段)实现原子更新,无需修改字段所属类的代码,灵活性极高
  • 需满足两个条件:字段必须是 volatile 修饰的(保证可见性),且不能是 private 字段(封装性保护)
    • AtomicIntegerFieldUpdater:原子更新对象的 volatile int 字段
    • AtomicLongFieldUpdater:原子更新对象的 volatile long 字段
    • AtomicReferenceFieldUpdater:原子更新对象的 volatile 引用字段
public class BankAccount {
    // 普通属性
    private String name;
    // 正确做法:private + volatile
    private volatile int balance;

    private static final AtomicIntegerFieldUpdater<BankAccount> updater =
        AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "balance");

    // 修改 balance 值
    public void deposit(int amount) {
        // 只能通过原子操作更新
        updater.addAndGet(this, amount);
    }

    // 自增 balance 值
    public int increment() {
        updater.incrementAndGet(this);
    }

    // 提供安全的读取方法
    public int getBalance() {
        return updater.get(this);
    }
}

5、性能优化原子类:LongAdder 与 DoubleAdder

在高并发计数场景下,AtomicLong 的自旋 CAS 会因冲突剧烈导致性能下降。JDK 1.8 引入 LongAdder 和 DoubleAdder,通过'分段锁'思想优化性能。

核心原理

  • LongAdder 将计数器拆分为多个'单元格'(Cell 数组),每个线程优先操作自己对应的单元格(通过线程哈希定位),降低 CAS 冲突概率
  • 最终获取结果时,累加所有单元格的值与基础值(base)
  • 当并发量较低时,直接操作 base 值,与 AtomicLong 性能接近
  • 并发量较高时,单元格分散冲突,性能远超 AtomicLong

实战对比:LongAdder 与 AtomicLong 性能

public class LongAdderVsAtomicLong {
    private static final AtomicLong atomicLong = new AtomicLong();
    private static final LongAdder longAdder = new LongAdder();
    private static final int THREAD_COUNT = 20;
    private static final int LOOP_COUNT = 1000000;

    public static void main(String[] args) throws InterruptedException {
        // 测试 AtomicLong 性能
        ExecutorService executor1 = Executors.newFixedThreadPool(THREAD_COUNT);
        long start1 = System.currentTimeMillis();
        for (int i = 0; i < THREAD_COUNT; i++) {
            executor1.submit(() -> {
                for (int j = 0; j < LOOP_COUNT; j++) {
                    atomicLong.incrementAndGet();
                }
            });
        }
        executor1.shutdown();
        executor1.awaitTermination(1, TimeUnit.MINUTES);
        long cost1 = System.currentTimeMillis() - start1;
        System.out.println("AtomicLong 耗时:" + cost1 + "ms,结果:" + atomicLong.get());

        // 测试 LongAdder 性能
        ExecutorService executor2 = Executors.newFixedThreadPool(THREAD_COUNT);
        long start2 = System.currentTimeMillis();
        for (int i = 0; i < THREAD_COUNT; i++) {
            executor2.submit(() -> {
                for (int j = 0; j < LOOP_COUNT; j++) {
                    longAdder.increment();
                }
            });
        }
        executor2.shutdown();
        executor2.awaitTermination(1, TimeUnit.MINUTES);
        long cost2 = System.currentTimeMillis() - start2;
        System.out.println("LongAdder 耗时:" + cost2 + "ms,结果:" + longAdder.sum());
    }
}

运行结果通常显示:高并发下 LongAdder 耗时仅为 AtomicLong 的 1/3~1/2,适合海量并发计数场景

五、CAS 与原子类的适用场景与对比

1、适用场景

  • 低冲突、短耗时操作:如计数器、状态标志位更新,CAS 无锁特性可最大化性能
  • 高并发计数/累加:优先使用 LongAdder/DoubleAdder,分段锁设计适配高冲突场景
  • 无锁数据结构实现:如无锁队列、无锁栈,基于 AtomicReference 封装 CAS 操作,提升并发吞吐量
  • 对象字段/引用的原子更新:使用字段更新原子类或引用类型原子类,无需修改原有类结构,灵活性高

2、与悲观锁(synchronized/ReentrantLock)的对比

对比维度CAS(乐观锁)/原子类悲观锁
核心思想'先操作,后检测冲突'(假设冲突很少发生)'先加锁,再操作'(假设冲突总发生)
线程行为无阻塞,冲突时自旋重试竞争失败时线程挂起(上下文切换)
性能开销轻量级,没有锁竞争和唤醒开销重量级,含上下文切换、锁调度开销
冲突处理高冲突下自旋耗 CPU,性能下降高冲突下稳定性更好,阻塞避免 CPU 空耗
功能范围仅支持单个变量/字段的原子操作支持任意代码块的原子性,功能更全面
ABA 问题存在,需额外机制解决不存在,互斥访问避免中间状态变更

目录

  1. 引言
  2. 一、什么是 CAS
  3. 二、CAS 的底层实现
  4. 1、CPU 原子指令支撑
  5. 2、Unsafe 类:Java 的底层“魔法工具”
  6. 3、volatile 关键字:保证内存可见性
  7. 三、CAS 的核心问题与解决方案
  8. 1、ABA 问题:值的“伪不变”陷阱
  9. 2、自旋开销问题:高冲突下的性能损耗
  10. 3、只能保证单个变量的原子操作
  11. 四、Java 原子类:CAS 的上层封装与实战
  12. 1、基本类型原子类
  13. 2、数组类型原子类
  14. 3、对象引用类型原子类
  15. 4、对象字段更新原子类
  16. 5、性能优化原子类:LongAdder 与 DoubleAdder
  17. 五、CAS 与原子类的适用场景与对比
  18. 1、适用场景
  19. 2、与悲观锁(synchronized/ReentrantLock)的对比

更多推荐文章

查看全部
  • 新版 PyCharm 远程连接 SSH 配置指南
  • Z-Image-Turbo 本地 AI 绘画工具 UI 实测与使用指南
  • C/C++ 算法:常见日期问题解析
  • 基于算法的 LLM 代码翻译新范式:解决意图丢失问题
  • Capacitor 跨平台打包实战:Web 应用构建原生 App
  • 前端股票行情 SDK:stock-sdk,支持浏览器与 Node.js
  • LeRobot 框架深度解析:架构、策略、硬件与数据流程
  • AIGC 内容创作全流程:文字、图像、音频与视频生成指南
  • AI 如何革新 PCB 设计:PADS 软件智能辅助功能解析
  • DCU BW1000 环境下 llama.cpp 推理 Qwen3-Coder-30B 实践与问题排查
  • C++ 继承机制详解:概念、访问控制与转换
  • 5 款零配置 AI 写作镜像推荐:低成本快速体验
  • Python OpenCV 入门与核心用法详解
  • AI 变现误区解析:为何掌握工具不等于获得收益
  • Generative Agents:交互式人类行为模拟研究
  • JDK 安装与环境配置实战指南
  • 银发族的 AI 助手:AIGC 如何陪伴父母防骗解闷
  • GTC 2026 前瞻:Rubin 平台与 AI 工厂化演进
  • C语言数据结构与算法基础:文件操作、排序查找及链表简介
  • Spring AI MCP Server 集成与示例

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online