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 的 AtomicIntegerAtomicBooleanAtomicLong 全是基于 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 底层实现
原子性 单个变量 代码块 / 方法
阻塞 不阻塞线程 阻塞线程
性能 低冲突时极高 高冲突时更稳定
适用场景 原子变量、简单并发 复杂业务、多变量同步
相关推荐
Chester_19997 小时前
CSP202203C.计算资源调度器
开发语言·数据结构·c++·蓝桥杯
EXI-小洲8 小时前
Java 操作 Word:字符串替换、图片插入、动态生成表格与API接口下载
java·开发语言·spring boot·word
会周易的程序员8 小时前
给 PLC 写一个字节码虚拟机:STVM 虚拟机架构设计
开发语言·c++·虚拟机·软plc·iec61131·stvm
机器视觉知识推荐、就业指导8 小时前
为什么老说“纯 Qt 没啥就业市场”?
开发语言·qt
telepan8 小时前
Qt 开发避坑与性能指南:如何优雅、安全地定义全局常量字符串?
开发语言·qt
m0_695251499 小时前
Qt MaintenanceTool 使用国内镜像加速下载(超详细教程)
开发语言·qt
2601_961901709 小时前
SpringBoot使用Nacos进行application.yml配置管理
java·spring boot·spring
何以解忧,唯有..10 小时前
LangChain 工具调用(Tool Calling)实战指南
java·前端·langchain
大衛說10 小时前
《Python 从入门到精通》系列总览与学习路线
开发语言·python·学习
QQ_216962909610 小时前
【源码编号:project79475】SpringBoot校内二手交易平台:商品发布、分类检索、留言交流、订单管理全流程实战
java·spring boot·后端