volatile通过强制主内存读写和插入内存屏障,既保证变量修改对所有线程立即可见,又禁止编译器与处理器对相关指令重排序,从而同时解决可见性与有序性问题。

volatile 关键字通过强制读写主内存 + 插入内存屏障,同时解决可见性与指令重排序问题。
怎么保证变量可见性
Java 内存模型(JMM)中,每个线程有自己私有的工作内存(如 CPU 缓存),普通变量的读写可能只发生在本地副本中,导致其他线程看不到最新值。volatile 改变这一行为:
- 每次读取 volatile 变量时,直接从主内存加载最新值,不使用缓存副本
- 每次写入 volatile 变量后,立即刷新回主内存,不等待写缓冲区合并
- 写操作会触发缓存一致性协议(如 MESI),使其他 CPU 核心中该变量的缓存行失效,后续读取被迫从主内存重新加载
怎么防止指令重排序
编译器和处理器为优化性能,可能调整语句执行顺序。volatile 不靠“魔法”,而是由 JVM 在字节码层面插入特定内存屏障(Memory Barrier)来约束重排:
- 写 volatile 变量前:插入 StoreStore 屏障,确保前面所有普通写操作已落地到主内存
- 写 volatile 变量后:插入 StoreLoad 屏障,阻止后续任意读/写操作被提到它前面
- 读 volatile 变量后:插入 LoadLoad + LoadStore 屏障,保证后续依赖该变量的读写不会被提前
典型例子是双重检查单例中的 instance = new Singleton()。没有 volatile 时,这行可能被重排为“分配内存 → 写引用 → 初始化对象”,导致其他线程拿到未初始化完成的对象;加了 volatile 后,重排被禁止,保障安全发布。
关键注意事项
volatile 是轻量级同步机制,不是万能锁:
- 不保证原子性:像
count++这类复合操作(读-改-写)仍可能出错,必须用synchronized或AtomicInteger - 仅保证变量本身可见:若修饰的是对象引用,只确保引用值更新可见,对象内部字段不自动具备 volatile 语义
- 不适合多写场景:通常适用于“一写多读”或状态标志位,多个线程同时写 volatile 变量无法避免竞态
- 不能用于依赖多个 volatile 变量的逻辑判断:例如
if (flag1 && flag2),即使两者都 volatile,也无法保证两个读之间的原子性或顺序一致性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











