volatile、AtomicInteger、CAS 三连问:并发无锁机制一篇通透
文章目录
- 《volatile、AtomicInteger、CAS 三连问:并发无锁机制一篇通透》
- 一、前言:从“加锁”到“无锁”的演进
- 二、volatile:可见性与有序性保障
- (1)作用:
- (2)典型示例:线程标志
- (3)底层原理
- 三、CAS:无锁的原子操作核心
- (1)概念
- (2)底层实现
- (3)优点
- (4)缺点
- 四、AtomicInteger:CAS 的“实用封装版”
- (1)定义
- (2)使用示例
- (3)源码核心
- (4)典型场景
- (5)口诀速记
- 五、三者关系与对比总结
- 六、面试总结
- 面试高频问题整理
- 结语
《volatile、AtomicInteger、CAS 三连问:并发无锁机制一篇通透》
一、前言:从“加锁”到“无锁”的演进
前几篇我们讲了 synchronized 和 ReentrantLock,它们属于加锁同步机制。
但在高并发场景下,加锁会带来线程阻塞、上下文切换等性能开销。
于是,Java 提供了更轻量的方案:
volatile + CAS + 原子类(Atomic)
这是并发中最“高频三连问”,也是面试官判断你是否真正理解底层并发的关键。
一句话速记:
volatile 保可见,
CAS 保原子,
Atomic 封装好工具。
二、volatile:可见性与有序性保障
(1)作用:
volatile 变量保证:
- 可见性:线程修改立即刷新主内存;
- 禁止指令重排:确保操作顺序执行;
- 但不保证原子性。
(2)典型示例:线程标志
volatile boolean running = true;public void run() {while (running) {// do something}
}
当其他线程设置
running = false时,循环会立即停止。
(3)底层原理
编译器在 volatile 读写前后插入内存屏障(Memory Barrier):
- 写入前:
StoreStoreBarrier - 写入后:
StoreLoadBarrier
保证数据及时写回主内存 + 禁止乱序。
三、CAS:无锁的原子操作核心
(1)概念
CAS(Compare And Swap,对比并交换)是一种原子操作指令:
“如果当前值等于期望值,则更新为新值;否则重试。”
示意:
while (true) {int old = value;if (compareAndSwap(value, old, old + 1))break;
}
(2)底层实现
- Java 层:
Unsafe.compareAndSwapInt() - CPU 层:使用汇编指令
cmpxchg实现原子操作(带总线锁)
(3)优点
- 无需加锁 → 高性能;
- 原子性由硬件保证。
(4)缺点
| 问题 | 说明 | 解决方案 |
|---|---|---|
| ABA 问题 | 值改为 A→B→A,CAS 误判成功 | 用版本号 AtomicStampedReference |
| 自旋开销大 | 失败后反复重试 | 限制自旋次数或退让 |
| 只能单变量 | 多变量同步难 | 使用锁或封装结构 |
四、AtomicInteger:CAS 的“实用封装版”
(1)定义
AtomicInteger 是 JUC 包下的原子类,用于无锁自增/自减操作:
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
}
private volatile int value;
它底层用的就是 CAS + volatile!
(2)使用示例
AtomicInteger count = new AtomicInteger(0);count.incrementAndGet(); // 自增
count.getAndAdd(5); // 加 5
count.compareAndSet(5, 10); // CAS 修改
(3)源码核心
public final int incrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
getAndAddInt 内部通过 CAS 循环实现原子性:
do {oldValue = getIntVolatile(obj, offset);newValue = oldValue + delta;
} while (!compareAndSwapInt(obj, offset, oldValue, newValue));
(4)典型场景
- 计数器(如并发请求数)
- 并发限流
- 异步任务统计
(5)口诀速记
Atomic 封装 CAS 计数器,
高并发下更高效。
五、三者关系与对比总结
| 项目 | volatile | CAS | AtomicInteger |
|---|---|---|---|
| 级别 | 关键字 | 算法机制 | 封装类 |
| 保证特性 | 可见性、有序性 | 原子性 | 可见性 + 原子性 |
| 是否加锁 | 否 | 否 | 否 |
| 是否阻塞 | 否 | 否 | 否 |
| 是否复合操作 | ❌ 否 | ❌ 否 | ✅ 是 |
| 应用场景 | 状态标志 | 原子更新 | 计数器、自增、自减 |
一句话总结:
volatile提供可见性,
CAS提供原子性,
Atomic把它们打包成易用工具。
六、面试总结
面试高频问题整理
| 问题 | 答案模板 |
|---|---|
| Q1:volatile 能保证原子性吗? | 不能,只保证可见性和有序性。 |
| Q2:CAS 是怎么保证线程安全的? | 利用 CPU 的原子指令 cmpxchg,操作不可中断。 |
| Q3:CAS 为什么会有 ABA 问题? | 值被改为 A→B→A,CAS 检测不到变化。 |
| Q4:AtomicInteger 为什么高效? | 基于 CAS + volatile,无锁且原子。 |
| Q5:CAS 一直失败会怎样? | 线程自旋重试,可能浪费 CPU,称为“自旋锁”。 |
结语
从 synchronized → Lock → AQS → CAS → Atomic,
Java 并发的演进,就是从“锁住别人”到“优化自己”。
掌握这三连问,面试时你不止能答出八股,更能解释底层逻辑。
下一篇,我会写——
《线程池 ThreadPoolExecutor 全解析:核心参数、执行流程与拒绝策略》,
这将进入并发编程的“高频压轴题”。
