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 底层实现
原子性 单个变量 代码块 / 方法
阻塞 不阻塞线程 阻塞线程
性能 低冲突时极高 高冲突时更稳定
适用场景 原子变量、简单并发 复杂业务、多变量同步
相关推荐
像我这样帅的人丶你还7 小时前
Java 后端详解(四):分页与搜索
java·javascript·后端
她的男孩7 小时前
数据权限为什么不能只靠注解?Forge 的 Mapper 层 SQL 改写源码拆解
java·后端·架构
tntxia8 小时前
Mybatis的日志输入
java
亦暖筑序10 小时前
Java 8老系统Browser Agent实战:三层拦截把AI操作后台变成可审计流程
java·后端·设计模式
用户2986985301413 小时前
Java 实现 Word 文档加密与权限解除
java·后端
Yeats_Liao13 小时前
14:Servlet中的页面跳转-Java Web
java·后端·架构
未秃头的程序猿13 小时前
告别"if-else地狱"!Java 21模式匹配,代码优雅了10倍
java·后端·面试
鹤望兰67514 小时前
字节跳动国际支付-后端开发-三面面经
java
Flittly14 小时前
【AgentScope Java新手村系列】(14)人机交互
java·spring boot·spring
RainCity14 小时前
Java Swing 自定义组件库分享(十二)
java·笔记·后端