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 底层实现
原子性 单个变量 代码块 / 方法
阻塞 不阻塞线程 阻塞线程
性能 低冲突时极高 高冲突时更稳定
适用场景 原子变量、简单并发 复杂业务、多变量同步
相关推荐
Bruce_Liuxiaowei1 分钟前
Python 实例赋值遮蔽类属性:一个从不报错的静默陷阱
开发语言·python·语法糖
滕州市燕猫虎计算机科技工作室个体工商户8 分钟前
IDEA:Command line is too long
java·ide·intellij-idea
步行cgn39 分钟前
Spring p 命名空间注入详解
java·前端·spring
YatHinLay42 分钟前
MyBatis 流式查询实战:ResultHandler 处理海量数据
java
天天被压力1 小时前
【跨市场数据实战 #08】可转债折价机会怎么筛:3个接口抓比价、列表和实时盘口
java·人工智能·python
aramae1 小时前
MySQL内置函数(7)
开发语言·笔记·后端·mysql·其他
张某布响丸辣2 小时前
附件下载的安全边界:路径白名单、NAS 哨兵文件与 Redis Lua 一次性令牌
java·redis·redis lua·nas哨兵
YatHinLay2 小时前
Spring Boot + Calcite 实现跨库查询
java
酷炫码神3 小时前
MosMos 创意效果与能力边界展示
java
Wang's Blog3 小时前
Java 接入Redis: 使用Jedis操作Redis
java·服务器·redis