Java学习随笔:volatile 关键字到底解决了什么问题(2026-09-19)
学习 Java 并发时,volatile 是我最早遇到、也最容易理解错的关键字之一。刚看到它的时候,我以为它和 synchronized 差不多,都是用来保证线程安全的,甚至一度觉得只要给共享变量加上 volatile,多线程环境下的并发问题就都解决了。直到认真读了 Java 内存模型的相关内容,我才明白,volatile 其实只解决了一部分问题,而且它解决的问题非常关键。
要理解 volatile,先要理解 Java 内存模型里“主内存”和“工作内存”的概念。每个线程都有自己的工作内存,线程对共享变量的读写,通常是在工作内存中进行的,只有在特定时机才会同步回主内存。这就带来两个后果:一是可见性问题,一个线程修改了共享变量,另一个线程可能迟迟看不到;二是有序性问题,编译器和处理器为了优化性能,可能会对指令重新排序,导致程序的执行顺序和代码书写顺序不一致。volatile 主要就是为了处理这两个问题。
先说可见性。给一个共享变量加上 volatile 之后,线程对它的写操作会立刻刷新到主内存,而其他线程在读取这个变量时,会重新从主内存中读取最新的值。这样一来,一个线程的修改就能及时被其他线程看到,避免了“一个线程已经改了,另一个线程还在用旧值”的情况。这一点在开关标志位这类场景里特别有用,比如用一个 volatile 的布尔变量来控制某个循环是否继续执行。
再说有序性。volatile 会在读写操作的前后插入内存屏障,阻止编译器和处理器把这些操作与周围的操作随意重排。对于被 volatile 修饰的变量,它能保证写操作之前的代码不会被排到写操作之后,读操作之后的代码不会被排到读操作之前。这种约束让 volatile 变量在某种意义上有了一个“同步点”的作用,也为一些轻量级的并发控制提供了基础。
但这里有一个很多人容易忽略的重点:volatile 只保证可见性和有序性,并不保证原子性。也就是说,像自增这样的复合操作,即使变量是 volatile 的,多线程下依然可能出问题。因为自增其实是“读取、加一、写回”三步,多个线程交错执行时,仍然可能丢失更新。要保证原子性,就需要借助 synchronized、Lock 或者原子类。这也是我最初理解错的地方,把 volatile 当成了万能锁。
这次学习还让我注意到,volatile 的使用往往和场景强相关。它轻量、开销小,适合用在状态标志、一次性安全发布这类场合;但真正涉及复合操作和临界区时,还是要靠更强的同步手段。把可见性、有序性和原子性这三件事分开来看,很多并发问题就有了更清晰的判断依据。
回看这一路的学习,我越来越觉得,并发之所以难,是因为它挑战了我们平时“代码从上到下顺序执行”的直觉。volatile 只是其中很小的一块,但它恰好把内存模型的几个核心概念串了起来。理解它,不只是记住一个关键字的用法,更是学会用线程的视角去重新审视每一行代码。

浙公网安备 33010602011771号