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

Java 中的 CAS 机制详解

CAS(比较并交换)是一种基于硬件原子操作的无锁多线程同步机制。Java 通过 Unsafe 类和 java.util.concurrent.atomic 包实现 CAS,提供 AtomicInteger 等原子类。CAS 包含 V、A、B 三个参数,支持 x86/ARM 等架构指令。优点包括高性能、非阻塞;缺点包括 ABA 问题、自旋开销及单变量限制。适用于计数器、状态标志及无锁数据结构,高竞争场景需谨慎使用。

SecGuard发布于 2026/3/28更新于 2026/6/434 浏览
Java 中的 CAS 机制详解

Java CAS(Compare And Swap)机制详解

一、概述

CAS(Compare And Swap,比较并交换) 是一种无锁(Lock-Free)的多线程同步机制,它基于硬件提供的原子性操作来实现线程安全。CAS 是现代并发编程中的核心技术,广泛应用于 Java 并发包中。

二、核心原理

1. 操作语义

CAS 操作包含三个参数:

  • V:要更新的内存地址(变量)
  • A:旧的预期值(Expected Value)
  • B:要设置的新值(New Value)
2. 执行流程
boolean cas(Object v, Object a, Object b) {
    if (v == a) {
        // 比较:当前值是否等于预期值
        v = b;
        // 交换:设置新值
        return true;
        // 成功
    } else {
        return false;
        // 失败
    }
}
3. 硬件支持

CAS 操作在硬件层面是原子的,主要通过以下方式实现:

  • x86/64 架构:CMPXCHG(Compare and Exchange)指令
  • ARM 架构:LDREX/STREX(Load-Exclusive/Store-Exclusive)指令
  • MIPS 架构:LL/SC(Load-Linked/Store-Conditional)指令

三、Java 中的 CAS 实现

1. sun.misc.Unsafe 类

Java 通过 Unsafe 类提供底层 CAS 操作:

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class CASExample {
    private static Unsafe unsafe;
    private volatile int value;
    private static long valueOffset;

    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
            valueOffset = unsafe.objectFieldOffset(CASExample.class.getDeclaredField("value"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

    // 自定义 CAS 操作
    public boolean compareAndSet(int expectedValue, int newValue) {
        return unsafe.compareAndSwapInt(this, valueOffset, expectedValue, newValue);
    }
}
2. java.util.concurrent.atomic 包

Java 提供了丰富的原子类,内部均基于 CAS 实现:

类名描述常用方法
AtomicInteger原子整型get(), set(), compareAndSet(), incrementAndGet()
AtomicLong原子长整型get(), addAndGet(), compareAndSet()
AtomicBoolean原子布尔型get(), compareAndSet()
AtomicReference<V>原子引用get(), set(), compareAndSet()
AtomicIntegerArray原子整型数组getAndSet(), compareAndSet()
AtomicStampedReference<V>带版本号的原子引用解决 ABA 问题

四、完整使用示例

示例 1:基础 CAS 操作
import java.util.concurrent.atomic.AtomicInteger;

public class BasicCASExample {
    private AtomicInteger counter = new AtomicInteger(0);

    public void increment() {
        int oldValue, newValue;
        do {
            oldValue = counter.get(); // 读取当前值
            newValue = oldValue + 1; // 计算新值
        } while (!counter.compareAndSet(oldValue, newValue)); // CAS 循环直到成功
    }

    public int getValue() {
        return counter.get();
    }

    public static void main(String[] args) throws InterruptedException {
        BasicCASExample example = new BasicCASExample();
        // 创建 10 个线程并发递增
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    example.increment();
                }
            });
            threads[i].start();
        }
        for (Thread t : threads) {
            t.join();
        }
        System.out.println("最终结果:" + example.getValue()); // 输出:10000
    }
}
示例 2:实现线程安全的栈
import java.util.concurrent.atomic.AtomicReference;

public class ConcurrentStack<T> {
    private static class Node<T> {
        final T value;
        Node<T> next;

        Node(T value) {
            this.value = value;
        }
    }

    private AtomicReference<Node<T>> top = new AtomicReference<>();

    // 无锁 push 操作
    public void push(T value) {
        Node<T> newNode = new Node<>(value);
        Node<T> oldHead;
        do {
            oldHead = top.get(); // 读取当前栈顶
            newNode.next = oldHead; // 新节点指向旧栈顶
        } while (!top.compareAndSet(oldHead, newNode)); // CAS 更新栈顶
    }

    // 无锁 pop 操作
    public T pop() {
        Node<T> oldHead, newHead;
        do {
            oldHead = top.get(); // 读取当前栈顶
            if (oldHead == null) {
                return null; // 栈为空
            }
            newHead = oldHead.next; // 新栈顶为下一个节点
        } while (!top.compareAndSet(oldHead, newHead)); // CAS 更新栈顶
        return oldHead.value;
    }

    public boolean isEmpty() {
        return top.get() == null;
    }
}
示例 3:实现自旋锁
import java.util.concurrent.atomic.AtomicBoolean;

public class SpinLock {
    private AtomicBoolean locked = new AtomicBoolean(false);

    public void lock() {
        // 自旋等待直到获取锁
        while (!locked.compareAndSet(false, true)) {
            // 可添加 Thread.yield() 或等待策略减少 CPU 占用
            Thread.yield();
        }
    }

    public void unlock() {
        locked.set(false);
    }

    public boolean tryLock() {
        return locked.compareAndSet(false, true);
    }
}
示例 4:带版本号的 CAS(解决 ABA 问题)

什么是 ABA 问题: 指内存值由 A 变为 B,又变回 A,CAS 无法感知变化。

import java.util.concurrent.atomic.AtomicStampedReference;

public class ABASolutionExample {
    public static void main(String[] args) {
        // 创建初始值,使用静态变量避免自动装箱创建新对象
        final Integer value100 = 100;
        final Integer value200 = 200;
        final Integer value300 = 300;

        // 初始值 100,版本号 0
        AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(value100, 0);
        int[] stampHolder = new int[1];
        int oldValue = ref.get(stampHolder);
        int oldStamp = stampHolder[0];
        System.out.println("初始:值=" + oldValue + ", 版本=" + oldStamp);

        // 模拟 ABA
        // ====================================================================
        // 注意:不能写成这样,否则 Integer 对象会被缓存,导致版本号不变
        // 步骤 1:100 → 200 ref.compareAndSet(100, 200, 0, 1); 这里的 100 和 200 都是自动装箱的 Integer 对象
        // 步骤 2:200 → 100 ref.compareAndSet(200, 100, 1, 2); 这里的 200 是新创建的 Integer 对象,与步骤 1 中的 200 不是同一个对象
        // ====================================================================

        // A -> B
        int[] stampHolder1 = new int[1];
        Integer currentValue1 = ref.get(stampHolder1);
        ref.compareAndSet(currentValue1, value200, stampHolder1[0], stampHolder1[0] + 1);

        // B -> A
        int[] stampHolder2 = new int[1];
        Integer currentValue2 = ref.get(stampHolder2);
        ref.compareAndSet(currentValue2, value100, stampHolder2[0], stampHolder2[0] + 1);

        // 尝试 CAS(会失败,因为版本变了)
        boolean success = ref.compareAndSet(oldValue, value300, oldStamp, oldStamp + 1);
        System.out.println("结果:" + (success ? "成功" : "失败"));
        System.out.println("原因:版本从" + oldStamp + "变为" + ref.getStamp());
    }
}

五、CAS 的优缺点

✅ 优点
  1. 高性能:无锁操作,避免线程上下文切换
  2. 非阻塞:线程不会挂起,减少死锁风险
  3. 可扩展性好:在高并发场景下性能优势明显
❌ 缺点
  1. ABA 问题:值从 A 改为 B 再改回 A,CAS 无法察觉
  2. 自旋开销:竞争激烈时 CPU 空转浪费资源
  3. 只能保证一个变量的原子性:无法保证多个变量的复合操作
  4. 实现复杂:正确实现无锁算法难度较大

六、CAS 与锁的性能对比

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

public class CASvsLockBenchmark {
    private static final int THREAD_COUNT = 10;
    private static final int ITERATIONS = 1000000;

    // 测试方法
    private static long benchmark(Runnable task) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
        Thread[] threads = new Thread[THREAD_COUNT];
        long start = System.currentTimeMillis();
        for (int i = 0; i < THREAD_COUNT; i++) {
            threads[i] = new Thread(() -> {
                task.run();
                latch.countDown();
            });
            threads[i].start();
        }
        latch.await();
        long end = System.currentTimeMillis();
        return end - start;
    }

    public static void main(String[] args) throws InterruptedException {
        // 1. 测试 synchronized
        Object lock = new Object();
        int[] syncCounter = {0};
        long syncTime = benchmark(() -> {
            for (int j = 0; j < ITERATIONS; j++) {
                synchronized (lock) {
                    syncCounter[0]++;
                }
            }
        });

        // 2. 测试 ReentrantLock
        ReentrantLock reentrantLock = new ReentrantLock();
        int[] lockCounter = {0};
        long lockTime = benchmark(() -> {
            for (int j = 0; j < ITERATIONS; j++) {
                reentrantLock.lock();
                try {
                    lockCounter[0]++;
                } finally {
                    reentrantLock.unlock();
                }
            }
        });

        // 3. 测试 CAS (AtomicInteger)
        AtomicInteger atomicCounter = new AtomicInteger(0);
        long atomicTime = benchmark(() -> {
            for (int j = 0; j < ITERATIONS; j++) {
                atomicCounter.incrementAndGet();
            }
        });

        System.out.println("测试结果(越低越好):");
        System.out.println("synchronized: " + syncTime + "ms");
        System.out.println("ReentrantLock: " + lockTime + "ms");
        System.out.println("CAS (AtomicInteger): " + atomicTime + "ms");

        // 验证结果
        System.out.println("\n最终计数验证:");
        System.out.println("synchronized: " + syncCounter[0]);
        System.out.println("ReentrantLock: " + lockCounter[0]);
        System.out.println("CAS: " + atomicCounter.get());
    }
}

典型测试结果(10 个线程,每个递增 100 万次):

  • synchronized: ~1200ms
  • ReentrantLock: ~800ms
  • CAS (AtomicInteger): ~400ms

七、CAS 的适用场景

✅ 适合使用 CAS 的场景
  1. 计数器:AtomicInteger、AtomicLong
  2. 状态标志:AtomicBoolean
  3. 无锁数据结构:栈、队列、集合
  4. 资源引用管理:对象池、连接池
  5. 版本控制:乐观锁实现
❌ 不适合使用 CAS 的场景
  1. 复杂业务逻辑:需要多个变量原子更新
  2. 竞争激烈场景:大量线程频繁 CAS 失败
  3. 需要阻塞等待:线程需要等待某些条件

八、最佳实践

1. 使用现有原子类
// ✅ 优先使用 JDK 提供的原子类
AtomicInteger counter = new AtomicInteger(0);
AtomicReference<User> userRef = new AtomicReference<>();
2. 避免过度自旋
// ❌ 纯自旋,CPU 消耗大
while (!atomicRef.compareAndSet(oldValue, newValue)) {
    // 空循环
}

// ✅ 添加退避策略
int retries = 0;
while (!atomicRef.compareAndSet(oldValue, newValue)) {
    if (retries++ > 100) {
        Thread.yield();
    }
    // 或者使用指数退避
}
3. 解决 ABA 问题
// 使用带版本号的原子引用
AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(0, 0);
// 或者使用 AtomicMarkableReference
AtomicMarkableReference<Integer> markedRef = new AtomicMarkableReference<>(0, false);
4. 组合操作使用循环 CAS
public void addIfLessThan(int max) {
    int current;
    int newValue;
    do {
        current = atomicInt.get();
        if (current >= max) {
            return; // 条件不满足
        }
        newValue = current + 1;
    } while (!atomicInt.compareAndSet(current, newValue));
}

九、总结

CAS 是现代并发编程的基石,它通过硬件支持的原子操作实现了高效的无锁同步。在实际开发中:

  1. 优先选择:使用 java.util.concurrent.atomic 包提供的原子类
  2. 注意限制:理解 CAS 的局限,特别是 ABA 问题和自旋开销
  3. 性能考量:在低到中度竞争场景使用 CAS,高度竞争时考虑其他方案
  4. 正确使用:复杂操作使用循环 CAS 模式,必要时添加退避策略

通过合理使用 CAS,可以在保持线程安全的同时获得比传统锁机制更好的性能,特别是在多核处理器和高并发场景下。

目录

  1. Java CAS(Compare And Swap)机制详解
  2. 一、概述
  3. 二、核心原理
  4. 1. 操作语义
  5. 2. 执行流程
  6. 3. 硬件支持
  7. 三、Java 中的 CAS 实现
  8. 1. sun.misc.Unsafe 类
  9. 2. java.util.concurrent.atomic 包
  10. 四、完整使用示例
  11. 示例 1:基础 CAS 操作
  12. 示例 2:实现线程安全的栈
  13. 示例 3:实现自旋锁
  14. 示例 4:带版本号的 CAS(解决 ABA 问题)
  15. 五、CAS 的优缺点
  16. ✅ 优点
  17. ❌ 缺点
  18. 六、CAS 与锁的性能对比
  19. 七、CAS 的适用场景
  20. ✅ 适合使用 CAS 的场景
  21. ❌ 不适合使用 CAS 的场景
  22. 八、最佳实践
  23. 1. 使用现有原子类
  24. 2. 避免过度自旋
  25. 3. 解决 ABA 问题
  26. 4. 组合操作使用循环 CAS
  27. 九、总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • NWPU VHR-10 遥感目标检测数据集实战指南
  • OpenClaw+Qwen3.5:构建本地化 AI 数字员工助手
  • AIOps 实践:利用 Dify 与 LangBot 构建飞书智能体机器人
  • Android 组件化架构设计与实现方案详解
  • 近半年无人机与大模型结合的 8 项核心研究
  • SDXL Prompt Styler 提示词风格增强工具使用指南
  • Win10 彻底关闭 Microsoft 365 Copilot 弹窗的 6 种方法
  • LLaMA Factory 大模型训练与微调指南
  • Unity3D 粒子系统核心模块实战:Velocity、Noise 与生命周期控制
  • Cogito-v1-preview-llama-3B:工业设备故障日志分析与维修建议
  • Replay AI 翻唱工具使用指南与汉化配置
  • 飞牛 NAS 原生 WebDAV 挂载 115 网盘配置指南
  • 2024 年国内主流 AI 大模型平台全方位测评
  • C++ 继承机制详解:实现栈、同名隐藏与派生类默认成员函数
  • Amazon SageMaker 部署 AIGC 应用:训练优化及 Web 前端集成实践
  • Llama-3.2-3B 本地部署搭建 AI 写作助手
  • Milvus 向量数据库实战:Attu 可视化安装与 Python 整合指南
  • 命令行大模型交互工具 MCPHost 配置与实战指南
  • 行星减速器原理、计算与 C++ 实现
  • 数据结构基础:树的概念与二叉树详解

相关免费在线工具

  • 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