volatile不实现乐观锁,仅提供cas所需的内存可见性保障;真正实现乐观锁的是cas操作,它通过比较并交换确保原子性,而volatile保证每次读写都基于主内存且禁止重排序。

volatile 本身不实现乐观锁,它只是为 CAS 提供必要的内存可见性保障;真正实现乐观锁的是 CAS 操作(如 AtomicInteger.compareAndSet),而 volatile 确保其他线程能及时看到变量的最新值。
volatile 的作用:保证可见性,不保证原子性
在多线程环境下,普通变量可能被线程缓存在本地工作内存中,导致一个线程修改了值,另一个线程看不到。用 volatile 修饰后:
- 每次读取都从主内存获取最新值
- 每次写入都立即刷回主内存
- 禁止指令重排序(对 volatile 写之前的操作,不能重排到写之后)
但注意:volatile 无法防止“读-改-写”这类复合操作的竞态条件,比如 i++,仍需 CAS 或 synchronized。
CAS 是乐观锁的核心机制
CAS(Compare-And-Swap)是 CPU 提供的原子指令,在 Java 中由 Unsafe.compareAndSwapInt 等方法封装,AtomicInteger、AtomicReference 等类内部都基于它。
典型流程是:
- 读取当前值(volatile 读,确保拿到最新)
- 计算新值
- 调用 compareAndSet(expected, updated):仅当当前值等于 expected 时,才更新为 updated,并返回 true
如果失败,说明期间被其他线程修改过,可重试(自旋)或放弃。
手写一个简单的乐观锁计数器示例
下面是一个基于 volatile + CAS 实现的线程安全计数器(非阻塞):
public class OptimisticCounter {
private volatile int value = 0; // volatile 保证可见性
public boolean increment() {
int current;
int next;
do {
current = value; // volatile 读,一定看到最新值
next = current + 1;
// CAS 成功则退出,失败则重试
} while (!compareAndSet(current, next));
return true;
}
// 模拟 CAS:实际应使用 Unsafe 或 AtomicXxx 类
private boolean compareAndSet(int expected, int update) {
// 注意:此处仅为示意,真实 CAS 必须是原子的
// Java 中应直接使用 new AtomicInteger().compareAndSet(...)
if (value == expected) {
value = update; // volatile 写,保证其他线程可见
return true;
}
return false;
}
}
⚠️ 实际开发中不要自己实现 compareAndSet,应直接使用 AtomicInteger 等 JDK 原生类,它们底层调用 Unsafe,才是真正原子的。
为什么必须搭配 volatile?
假设不用 volatile:
- 线程 A 读取 value = 10,开始计算 next = 11
- 线程 B 同时将 value 改为 11 并写回主内存
- 线程 A 因为读的是旧缓存值,仍用 expected=10 调用 CAS → 成功覆盖为 11,但丢失了 B 的一次更新
加上 volatile 后,A 在每次循环开头都会重新从主内存读 value,从而感知到 B 的修改,使 CAS 失败并触发重试,保证逻辑正确。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











