Skip to content
极高进阶

一句话答案

volatile 保证可见性(写入立即刷新主内存)和有序性(禁止指令重排),但不保证原子性,底层靠内存屏障实现。

核心要点

volatile 的两大作用:

1. 可见性(Visibility)

  • volatile 变量的写操作立刻刷新到主内存
  • volatile 变量的读操作每次从主内存读取最新值(不使用 CPU 缓存/工作内存)
  • 底层:通过 lock 前缀指令,使写操作同时令其他 CPU 的缓存行失效(缓存一致性协议 MESI)

2. 禁止指令重排序(有序性)

  • 编译器和 CPU 出于性能优化会对指令重排序,volatile 通过内存屏障(Memory Barrier)禁止特定方向的重排序
  • 规则:volatile 写操作之前的代码不能被重排到写之后;volatile 读之后的代码不能被重排到读之前

经典应用——双重检查单例(DCL):

java
class Singleton {
    // 必须加 volatile!防止"半初始化对象"被另一个线程看到
    private static volatile Singleton instance;
    
    public static Singleton getInstance() {
        if (instance == null) {              // 第一次检查(无锁)
            synchronized (Singleton.class) {
                if (instance == null) {      // 第二次检查(持锁)
                    instance = new Singleton();
                    // 如果没有 volatile,new Singleton() 分三步:
                    // 1. 分配内存
                    // 2. 初始化对象
                    // 3. 将 instance 指向内存
                    // 2 和 3 可能被重排:另一线程看到 instance != null 但对象未初始化
                }
            }
        }
        return instance;
    }
}

volatile vs synchronized:

维度volatilesynchronized
可见性✅ 保证✅ 保证
有序性✅ 禁止重排✅ 进出同步块有内存屏障
原子性❌ 不保证(i++ 不安全)✅ 保证同步块内原子执行
互斥❌ 不互斥(多线程可同时读写)✅ 同一时刻只有一个线程执行
性能轻量(无锁,只有内存屏障)重量(偏向→自旋→阻塞,有上下文切换风险)
适用场景状态标志位、单次赋值复合操作(先读后写)、互斥区
追问与易错

追问方向:

  • volatile 能保证原子性吗?举个例子?(不能,i++ 是读-改-写三步)
  • volatile 禁止重排的底层原理?(编译器屏障 + CPU 内存屏障)
  • DCL 单例为什么需要 volatile?(防止对象未完全初始化就被其他线程使用)

易错点:

  • ❌ "volatile 可以替代 synchronized"——volatile 不保证原子性
  • ❌ "volatile 变量读写有锁"——无锁,靠 CPU 缓存一致性协议(MESI)

💡 记忆锚点

volatile 是内存世界的"实时广播":写了立刻广播给所有核心(可见性),并且禁止广播前后的消息乱序(有序性)。但它管不了"读-改-写"这种三步操作的原子性——i++ 不是一句话而是三句话,中间随时可能被插嘴。DCL 单例不加 volatile,别人可能拿到一个"壳子造好了但内部还没装修"的半成品对象。