java 中 volatile 的核心规则是保证可见性和禁止指令重排序,但不保证原子性。具体做法是在变量声明时添加 volatile 关键字,这样线程对该变量的写操作会立即刷新到主内存,读操作会从主内存重新加载,且编译器不会对该变量的读写操作进行重排序。

volatile 如何保证可见性
volatile 通过内存屏障和缓存一致性协议实现可见性。当线程写入 volatile 变量时,会插入 StoreStore 和 StoreLoad 屏障,确保之前的写操作对其他线程立即可见;读取时插入 LoadLoad 和 LoadStore 屏障,确保后续读操作能看到最新的值。

public class VolatileExample {
private volatile boolean flag = false;
public void writer() {
flag = true; // 写入 volatile 变量
}
public void reader() {
if (flag) { // 读取 volatile 变量
System.out.println("Flag is true");
}
}
}
在上述代码中,writer() 方法将 flag 设为 true,reader() 方法检查 flag。由于 flag 是 volatile 的,一旦 writer() 执行完毕,reader() 能立即看到最新值。
volatile 如何禁止指令重排序
volatile 通过插入内存屏障禁止指令重排序。在写入 volatile 变量前,插入 StoreStore 屏障,确保前面的写操作不会被重排序到后面;在读取 volatile 变量后,插入 LoadLoad 屏障,确保后面的读操作不会被重排序到前面。

public class DoubleCheckedLocking {
private static volatile Instance instance;
public static Instance getInstance() {
if (instance == null) { // 第一次检查
synchronized (DoubleCheckedLocking.class) {
if (instance == null) { // 第二次检查
instance = new Instance(); // 写入 volatile 变量
}
}
}
return instance;
}
}
在双重检查锁定模式中,instance 是 volatile 的,防止了 new Instance() 的重排序,确保其他线程看到的 instance 是完全初始化的对象。
volatile 不保证原子性的具体表现
volatile 不保证复合操作的原子性,如自增操作 i++。即使 i 是 volatile 的,i++ 仍可能因多线程竞争导致数据不一致。
public class VolatileCounter {
private volatile int count = 0;
public void increment() {
count++; // 非原子操作
}
public int getCount() {
return count;
}
}
在上述代码中,count++ 包含读取、修改和写入三个步骤,多线程环境下可能导致计数错误。需使用 AtomicInteger 或 synchronized 保证原子性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











