static解决变量共享一份的问题,volatile解决修改后对所有线程立即可见的问题;二者互补用于多线程状态共享,但volatile不保证原子性。

static 和 volatile 经常一起用,但不是因为“必须搭配”,而是因为它们解决的是不同层面的问题,恰好在多线程共享状态场景中形成互补:static 让变量属于类、全局一份;volatile 让这个共享变量的修改对所有线程立即可见。
static 解决“谁来存”——共享一份
被 static 修饰的变量随类加载而初始化,在方法区(元空间)中只有一份内存,所有线程访问的都是同一个变量。比如计数器、开关标志、配置缓存等,天然需要跨实例、跨线程共用。
但仅靠 static 不够——JVM 允许线程把 static 变量的值缓存在自己的工作内存里,不及时从主内存刷新。这就导致一个线程改了值,另一个线程可能长时间看不到更新。
volatile 解决“看不看得见”——强制可见性
volatile 的核心作用是禁止指令重排序 + 强制读写都直达主内存。加上 volatile 后,每次读取都从主内存重新加载,每次写入都立即刷回主内存。这样,一个线程对 static volatile 变量的修改,其他线程下一次读就能立刻感知到。
典型例子是线程控制标志:
- public static volatile boolean running = true;
- Worker 线程 while(running) { ... } 循环执行
- 主线程调用 stop() → running = false
- 没有 volatile:子线程可能永远卡在循环里(读的是自己缓存的旧值)
- 加上 volatile:子线程下次判断时必然读到 false,正常退出
它们不解决“能不能同时改”——原子性仍需额外保障
volatile 保证可见性,但不保证原子性。比如 i++ 操作包含“读-改-写”三步,即使 i 是 static volatile,多个线程并发执行 i++ 仍可能丢失更新。
所以这类场景要区分清楚:
- 只做“通知”“开关”“状态标记”(如 running、isReady、configVersion)→ static volatile 足够
- 要做“累加”“计数”“复合操作”(如 number++、list.add())→ 需配合 synchronized、AtomicInteger 或 ReentrantLock
什么时候不需要 volatile?
如果变量只被单一线程写、其他线程只读(且写操作发生在读之前),或者变量本身是 final static 常量,那就不需要 volatile。另外,某些明确使用锁(synchronized / Lock)保护的 static 变量,其可见性已由锁机制保证,也不必再加 volatile —— 锁的释放和获取本身就具有与 volatile 相同的内存语义。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











