JMM 进阶:彻底理解 CAS 实现原理

Java CAS 详解

CAS 是 Compare And Swap(比较并交换) 的缩写,是 Java 实现无锁并发 的核心原理,也是 java.util.concurrent.atomic 包(原子类)的底层实现机制,用来在不使用 synchronized 的情况下,保证多线程修改共享变量的线程安全

一、CAS 是什么

先比较内存中的值和预期值是否一致,如果一致,就把新值替换进去;如果不一致,说明有其他线程修改过,本次操作失败,不做替换

整个比较 + 替换 是一个原子操作(CPU 指令级保证,不可中断),所以多线程下不会出现数据错乱

二、CAS与乐观锁

乐观锁(Optimistic Lock)是一种并发控制思想

它的核心假设是:数据冲突很少发生

因此:

不加锁

先操作

提交时再检查是否有人修改过

如果发现冲突:

失败

重试

例如:

账户余额 = 100

线程A:

读取余额100

线程B:

读取余额100

A扣款:

100 → 90

提交成功

B也准备扣款:

100 → 80

此时检查发现:

余额已经不是100

说明:有人改过,操作失败

重新读取再计算

这就是乐观锁思想:先干活,最后检查

三、CAS 执行流程

CAS 操作需要 3 个值:

CAS(V, E, N)

V:内存中的实际值(Value)
E:期望值(Expected)
N:要更新的新值(New)

举例:

java 复制代码
AtomicInteger count = new AtomicInteger(10);

线程A执行:

java 复制代码
count.compareAndSet(10, 11);

内部过程:

当前值 = 10

期望值 = 10

新值 = 11

当前值10 == 期望值10

更新成功

结果:

java 复制代码
count = 11

如果线程B同时执行:

java 复制代码
count.compareAndSet(10, 12);

此时:

当前值 = 11

期望值 = 10

当前值11 != 期望值10

更新失败

返回:false

四、CAS 的底层实现

  1. Java 层面:sun.misc.Unsafe 类提供 CAS 方法
  2. 底层:CPU 原语指令 (如 cmpxchg),由硬件保证原子性
  3. 特点:无锁、轻量级、不会阻塞线程

Java 的 CAS 最终调用 JVM 的 Native 方法

例如:AtomicInteger

源码(JDK8):

java 复制代码
public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(
        this,
        valueOffset,
        expect,
        update);
}

这里调用:

java 复制代码
Unsafe.compareAndSwapInt()

JDK9以后:

java 复制代码
VarHandle.compareAndSet()

逐步替代 Unsafe

五、CAS 经典应用:原子类

Java 的 AtomicInteger、AtomicBoolean、AtomicLong 全是基于 CAS 实现的

示例代码:

java 复制代码
import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {
    // 原子整数,线程安全
    private static AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) count.incrementAndGet(); // 底层就是 CAS
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) count.incrementAndGet();
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println(count.get()); // 一定输出 2000,线程安全
    }
}

incrementAndGet() 底层就是循环 CAS,直到修改成功

六、CAS 的优点

  1. 轻量级:不需要加锁、解锁,没有线程阻塞 / 唤醒开销
  2. 性能高:高并发、冲突少的场景,远优于 synchronized
  3. 无死锁:因为根本不加锁

七、CAS 的 3 大缺点

1. 自旋消耗 CPU(循环 CAS)

如果并发冲突严重,CAS 会一直循环重试,长时间占用 CPU

java 复制代码
// 底层伪代码
do {
    读取当前值 V;
    计算新值 B;
} while (!CAS(V, B));  // 失败就无限重试

2. 只能保证一个变量的原子操作

CAS 一次只能操作一个共享变量,无法像锁一样同时保证多个变量的原子性。(解决方案:AtomicReference 包装成对象)

3. ABA 问题

场景:

  1. 内存值 V = A
  2. 线程 1 把 A → B
  3. 线程 2 又把 B → A
  4. 线程 3 执行 CAS:发现 V 还是 A,以为没被修改过,直接修改成功

问题:变量被修改过又改回来,CAS 无法感知

解决方案 :版本号机制 (AtomicStampedReference)每次修改都带上版本号,比较时不仅比值,还要比版本号

八、CAS vs synchronized

特性 CAS synchronized
实现 无锁,CPU 原子指令 阻塞锁,JVM 底层实现
原子性 单个变量 代码块 / 方法
阻塞 不阻塞线程 阻塞线程
性能 低冲突时极高 高冲突时更稳定
适用场景 原子变量、简单并发 复杂业务、多变量同步
相关推荐
Eiceblue2 分钟前
用 Python 给 Word 文档加水印:文字、图片与批量处理
开发语言·vscode·python·word
j7~7 分钟前
【C++ 面试笔记】(篇二):类与数据抽象 & STL 容器与算法
开发语言·c++·面试·stl容器和算法·类和数据抽象
Fuyo_111925 分钟前
深入解析 C++ unordered_set 与 unordered_map 的核心特性与性能实战
开发语言·c++
从琳开始98935 分钟前
从Socket到UDP回显服务器:Java网络编程完整指南
java·网络·网络协议·udp·java-ee
ShineWinsu1 小时前
对于Qt:QPushButton常见属性的解析
开发语言·c++·qt·api·gui·按钮·qpushbutton
外贸GEO优化1 小时前
外贸独立站的可抓取性:JS 渲染、懒加载与无限滚动到底影响什么
开发语言·javascript·ecmascript
飞浪纪元[FWC–FE]1 小时前
Qt 工程创建与 CMake / qmake 构建规范
开发语言·数据库·qt
后台模板学习1 小时前
Go Web开发中任务调度框架的源码解析与高可用实现方案
开发语言·后端·golang
西瓜拿铁好喝1 小时前
开源和闭源 AI 办公工具,分别适合谁
java·前端·数据库