volatile是什么
volatile是Java虚拟机提供的轻量级的同步机制,是 Java 的一个关键字。volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。 当某个变量是共享变量,且这个变量是被 volatile 修饰的,那么在修改了这个变量的值之后,再读取该变量的值时,可以保证获取到的是修改后的最新的值,而不是过期的值。
- 保证可见性
- 不保证原子性
- 禁止指令重排
内存可见性(Memory Visibility)
标记为 volatile
的变量,对一个线程的写操作,会立即被刷新到主内存;对另一个线程的读操作,则总是从主内存中获取最新的值。这样一来,一个线程对 volatile
变量的修改,能够被其他线程及时"看见"。
java
class Worker extends Thread {
private volatile boolean running = true;
public void run() {
while (running) {
// 工作逻辑
}
}
public void shutdown() {
running = false;
}
}
禁止指令重排序(Prevents Reordering)
在 JMM 中,普通操作可因编译器或处理器优化而乱序执行,但对 volatile
变量的读写具有"内存屏障"效果:
- 写入
volatile
变量之前的所有操作,在内存屏障之前,必须先执行完成。 - 读取
volatile
变量之后的所有操作,在内存屏障之后,才能开始执行。
这保证了对 volatile
变量前后的操作不会被重排序,从而提供了一定的执行顺序。
java
class Singleton {
private static volatile Singleton instance; // 必须 volatile!
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // 禁止重排序,避免返回未初始化对象
}
}
}
return instance;
}
}
底层原理
屏障类型 | 作用 |
---|---|
LoadLoad | 禁止 volatile 读与后续普通读重排序 |
LoadStore | 禁止 volatile 读与后续普通写重排序 |
StoreStore | 禁止 volatile 写之前的普通写与 volatile 写重排序(确保写操作可见) |
StoreLoad | 禁止 volatile 写与后续 volatile 读/写重排序(全能屏障,开销最大) |
在每个volatile读操作的后面插入一个LoadLoad屏障;
在每个volatile读操作的后面插入一个LoadStore屏障。
在每个volatile写操作的前面插入一个StoreStore屏障;
在每个volatile写操作的后面插入一个StoreLoad屏障;
需要注意的是:volatile写是在前面和后面分别插入内存屏障,而volatile读操作是在后面插入两个内存屏障
局限性
不保证复合操作的原子性 对 volatile
变量进行 ++
、--
、+=
等不是原子操作,仍需额外同步或使用原子类(AtomicInteger
等)。
不能替代锁 对于涉及多个变量之间的一致性维护,volatile
无法保证"原子性和可见性的一致性",必须使用锁(synchronized
、ReentrantLock
)或原子类。
性能开销 虽然比锁轻量,但频繁对 volatile
变量的读写仍会引入内存屏障,影响 CPU 的乱序优化和缓存一致性性能。只在确有需求时使用。
不支持"等待-通知"机制 与 synchronized
不同,volatile
无法实现 wait()
、notify()
等线程协调方法。