本文深入探讨 CPU 缓存、内存屏障与 volatile 关键字在并发编程中的应用。
这三者都是为了解决 '多线程下,数据不一致、执行顺序乱' 的问题。
一、CPU 缓存
在聊 volatile 和内存屏障之前,我们先来说一说 CPU 缓存 —— 因为 volatile 和内存屏障的所有设计,都是为了解决 'CPU 缓存带来的问题'。
- 多线程修改共享变量(比如 int count = 0),count 会先被加载到 CPU 缓存,CPU 修改缓存里的 count 后,不会立刻写回主内存;
- 其他线程读取 count 时,会从自己的 CPU 缓存里读,而不是主内存 —— 这就会导致:一个线程改了 count,另一个线程看不到(可见性问题);
- 甚至 CPU 会为了提升效率,打乱 '读缓存、写缓存、写主内存' 的顺序(指令重排序),导致多线程执行顺序混乱(有序性问题)。
2. CPU 缓存的核心问题(并发 bug 的根源)
这两个问题,就是 volatile 和内存屏障要解决的:
- 可见性问题:CPU 缓存里的数据,不会实时同步到主内存;其他 CPU(其他线程)不会实时读取主内存的最新数据,导致多个线程看到的 '同一个变量' 不一样;
- 有序性问题:CPU 会打乱指令执行顺序(重排序),单线程下没问题,但多线程下会导致逻辑错乱。
举个简单例子(可见性问题):
// 线程 1 修改 count,线程 2 读取 count
private static int count = 0;
public static void main(String[] args) {
// 线程 1:修改 count 为 1
new Thread(() -> {
count = 1;
System.out.println("线程 1 修改 count 为 1");
}).start();
// 线程 2:读取 count
new Thread(() -> {
while (count == 0) {
// 一直循环,直到 count != 0
}
System.out.println("线程 2 读取到 count = " + count);
}).start();
}
实际运行中,可能出现:线程 1 已经修改 count 为 1(修改的是自己 CPU 缓存里的 count),但没写回主内存;线程 2 一直从自己的 CPU 缓存里读 count=0,陷入死循环 —— 这就是 CPU 缓存导致的可见性问题。
3. 缓存命中 / 缺失
- 缓存命中:CPU 要读 / 写的数据,正好在缓存里,速度极快;
- 缓存缺失:CPU 要读 / 写的数据,不在缓存里,速度变慢,会把主内存的数据加载到缓存(缓存填充)。

