从 JMM 到 CAS:拆解 Java 并发编程的内存规范与原子操作原理
从 JMM 到 CAS:拆解 Java 并发编程的内存规范与原子操作原理
摘要:本文聚焦Java并发编程的两大核心技术——JMM(Java内存模型)与CAS(比较并交换),系统解析其原理、应用及特性。整体内容为理解Java多线程同步逻辑、选择合适并发方案提供关键参考。
一、JMM(Java内存模型)

1. 定义与核心作用
JMM 定义了共享内存环境下多线程程序对内存读写操作的行为规范,通过这些规则约束内存读写行为,从而保证多线程场景下指令执行的正确性。
2. 内存区域划分
JMM 将内存逻辑上分为两类区域:
- 工作内存:属于线程私有的区域,每个线程都有自己独立的工作内存。
- 主内存:属于所有线程共享的区域,是线程间进行数据交互的“公共区域”。
3. 线程间的交互规则
线程之间相互隔离,线程与线程之间若要进行数据交互(比如共享变量的传递、同步),必须通过主内存来完成(而非线程间直接交互)。
在Java并发编程中,CAS(Compare and Swap,比较并交换)是一种实现无锁并发操作的核心机制,它通过硬件层面的原子指令保证操作的原子性,从而避免了传统锁机制带来的性能开销。
二、CAS
CAS的基本原理
CAS操作包含三个核心参数:
- 内存位置(V):需要操作的变量在内存中的地址;
- 预期原值(A):线程认为变量当前应该的值;
- 新值(B):如果变量当前值等于预期原值,线程希望将其更新为的值。
操作逻辑是:当且仅当内存位置V的值等于预期原值A时,才将V的值更新为新值B;否则不做任何操作。整个过程是原子的,由CPU的原子指令(如x86的cmpxchg)直接支持,无需加锁。
CAS在Java中的应用
Java通过sun.misc.Unsafe类提供了CAS的底层实现(如compareAndSwapInt、compareAndSwapLong等native方法),上层并发工具(如java.util.concurrent.atomic包下的类)基于此实现原子操作:
- 例如
AtomicInteger的incrementAndGet()方法(自增并返回新值),内部通过CAS循环实现:
线程会不断读取变量当前值public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; } // 底层CAS循环 public final int getAndAddInt(Object o, long offset, int delta) { int v; do { v = unsafe.getIntVolatile(o, offset); // 读取当前值(保证可见性) } while (!unsafe.compareAndSwapInt(o, offset, v, v + delta)); // 循环CAS直到成功 return v; }v,并尝试通过CAS将其更新为v+delta,如果失败(其他线程已修改)则重新读取并重试,直到成功。
CAS的优势
- 无锁开销:避免了
synchronized等锁机制的线程阻塞/唤醒、上下文切换等开销,在低并发场景下性能更优。 - 细粒度控制:可针对单个变量实现原子操作,比锁的粒度更细。
CAS的局限性
-
ABA问题:
当变量的值从A被修改为B,再改回A时,CAS会认为“值未变”而成功更新,但实际中间可能发生了其他操作(如链表节点被删除后重新插入)。
解决方式:通过版本号标记,如AtomicStampedReference,它同时维护“值”和“版本号”,CAS时需同时比较值和版本号。 -
自旋开销:
当高并发场景下多个线程竞争同一个变量时,CAS可能会频繁失败并进入循环重试(自旋),导致CPU资源浪费。
优化思路:自适应自旋(如JVM的偏向锁/轻量级锁逻辑),根据历史重试情况动态调整自旋次数。 -
只能保证单个变量的原子性:
CAS仅能对单个变量实现原子操作,若需对多个变量进行原子操作,仍需使用锁(如synchronized或ReentrantLock)。
总结
CAS是Java并发中实现无锁同步的关键技术,通过硬件原子指令保证操作的原子性,在java.util.concurrent包中被广泛应用(如原子类、并发容器等)。它在低并发场景下性能优异,但存在ABA问题、自旋开销等局限,实际使用中需结合场景选择合适的同步方案。
更多推荐
所有评论(0)