volatile关键字通过强制读写主内存和插入内存屏障,确保static变量修改后对所有线程立即可见,并禁止指令重排序;但它不保证复合操作的原子性。

静态变量本身是类级别共享的,所有线程访问的是同一块内存地址,但这不等于“修改后立刻可见”。JVM 允许线程把 static 变量缓存在自己的工作内存中,若不加干预,一个线程改了值,另一个线程可能永远读不到——volatile 就是用来打破这种缓存滞后的关键机制。
为什么 static 变量必须显式加 volatile 才能保证可见性
静态变量只是“共享地址”,不是“自动同步”。JVM 的内存模型(JMM)下,每个线程都有自己的工作内存(CPU 缓存或寄存器),普通 static 变量的读写仍可被缓存、重排序。volatile 强制:
- 每次读取都跳过工作内存,直接从主内存加载最新值
- 每次写入都立即刷新回主内存,不等线程退出或方法结束
- 插入读/写屏障,禁止编译器和 CPU 对该变量的指令重排序
典型写法与常见场景
声明时直接在 static 前加上 volatile,语法简洁但语义明确:
-
开关标志位:
private static volatile boolean running = true;—— 子线程通过while(running)安全轮询,主线程设为false后立即退出 -
单例初始化标记:
private static volatile Singleton instance;—— 配合双重检查锁(DCL),防止指令重排导致未初始化对象被其他线程使用 -
配置热更新通知:
private static volatile String configVersion;—— 外部更新后,各线程下次读取即得新值,无需锁或额外同步
volatile 不能做什么:关键限制要清楚
它只保可见性,不保原子性。以下操作即使加了 volatile,依然线程不安全:
立即学习“Java免费学习笔记(深入)”;
-
counter++(本质是读-改-写三步,中间可能被其他线程打断) -
list.add(x)(方法内部涉及多步状态变更) - 依赖多个变量协同判断的逻辑,如
if (status == READY && data != null)
这类情况需改用 AtomicInteger、synchronized 或 ReentrantLock。
和普通 static 变量对比的实际效果
没加 volatile 时,一个线程修改 static boolean flag = true,另一线程可能持续读到 false,陷入死循环;加了之后,只要写操作完成,读线程下一次读就一定能拿到 true —— 不需要 sleep、yield 或额外唤醒机制,纯粹靠 JMM 保障。


















