大家在 Java 并发编程中,经常被 volatile 和 CAS 搞晕: 什么时候用 volatile?什么时候用 CAS? volatile 能保护集合吗? CAS 能实现无锁安全的多读多写吗?
这篇文章用最简单的代码 + 最清晰的结论,一次性讲透。
一、volatile 到底能干嘛?
1.1 核心能力
保证可见性:一个线程修改,其他线程立刻看到
保证单次读、单次赋值原子性
不保证复合操作原子性(i++、if + 操作 + 赋值 都不行)
1.2 最经典用法:状态开关(一读一写)
public class Test {
private volatile boolean stop = false;
// 写线程(单个)
public void stop() {
stop = true;
}
// 读线程(单个/多个)
public void run() {
while (!stop) {
// 业务逻辑
}
}
}
✅ 安全 ✅ 无锁 ✅ volatile 天生支持
class Singleton {
private static volatile Singleton instance; // volatile 关键
public static Singleton getInstance() {
if (instance == null) { // 第一次检查(无锁)
synchronized (Singleton.class) { // 加锁!!!
if (instance == null) { // 第二次检查(有锁)
instance = new Singleton();
}
}
}
return instance;
}
}
二、volatile 做控制器:保护其他变量(一读一写)
volatile 可以作为开关,安全保护另一个共享变量(如 List)。
规则:
-
写线程:先写数据 → 最后修改 volatile
-
读线程:先判断 volatile → 再读数据
-
只能一读一写
public class SafeDemo {
private volatile boolean initialized = false;
private List<String> dataList = new ArrayList<>();
// 写线程(仅1个)
public void writer() {
if (!initialized) {
dataList.clear();
dataList.add("data1");
dataList.add("data2");
initialized = true;
}
}
// 读线程(仅1个)
public void reader() {
if (initialized) {
System.out.println(dataList.size());
initialized = false;
}
}
}
✅ 无锁安全 ✅ volatile 控制 + 共享变量保护 ❌ 不能多读(多个读线程会并发冲突)
三、volatile + List 正确用法:替换引用
volatile 只保证引用赋值可见 + 原子。
正确姿势:
-
写线程:新建 List → 赋值给 volatile
-
读线程:只读取引用
-
单写 + 多读安全
private volatile List<String> list = new ArrayList<>();
// 写线程(唯一写)
void write() {
List<String> newList = new ArrayList<>();
newList.add("a");
newList.add("b");
list = newList; // 安全
}
// 读线程(多个)
void read() {
List<String> tmp = list;
for (String s : tmp) {
System.out.println(s);
}
}
✅ 无锁、安全、高效
四、CAS 是什么?比 volatile 强在哪?
4.1 核心区别
-
volatile:保证可见性、单次读写原子
-
CAS:保证单个变量复合操作原子性(++、if+set)
4.2 CAS 能做什么
-
计数器 incrementAndGet()
-
条件更新 compareAndSet(expect, update)
-
无锁自旋锁
-
保护其他变量实现多读多写
五、CAS 实战:无锁实现 List 多读多写
利用 AtomicBoolean 做无锁互斥:
-
同一时间只有一个线程能写
-
其他写线程自旋等待
-
读线程必须等写完成
public class SafeListHandler {
private List<String> list = new ArrayList<>();
private final AtomicBoolean writing = new AtomicBoolean(false);
// 多写安全
public void write(String data) {
while (!writing.compareAndSet(false, true)) {
}
try {
list.add(data);
} finally {
writing.set(false);
}
}
// 多读安全
public List<String> read() {
while (writing.get()) {
}
return new ArrayList<>(list);
}
}
✅ 完全无锁 ✅ 多读多写安全 ✅ List 被安全保护
六、终极总结(面试 / 工作必背)
volatile
-
支持:单次读、单次赋值原子
-
支持:一读一写、多读一写
-
可做控制器:保护其他变量(只能一读一写)
-
不支持:复合操作、多写
CAS
-
支持:复合操作原子性(++、条件赋值)
-
支持:多读多写(单变量)
-
可做无锁自旋锁:保护其他变量(多读多写)
-
不支持:多变量同时原子
一句话口诀
-
volatile:单写多读,无锁可见
-
CAS:单变量原子,无锁互斥
-
想无锁安全多读多写:用 CAS 做自旋锁
-
volatile即使忙等待,也不能多读多写,因为不能解决原子性GETSET
因为写只能同时一个在操作,只有原子性 get/set 的变量,才能安全多写;
只要不是纯 get/set,多写必须用 CAS 或锁。
中断例外,中断没有并发问题;
volatile 和 cas 都是用于单变量。
