一文讲全volatile关键字

1. 背景与内存模型
- 缓存一致性问题:在多线程环境下,由于每个线程可能拥有自己的工作内存(如CPU高速缓存),对共享变量的修改可能不会立即被其他线程看到,导致数据不一致。
- 解决方案:硬件层面通过总线锁或缓存一致性协议(如MESI)解决,
volatile在Java中提供了语言层面的支持。
2. 并发编程的三大问题
- 原子性:一个操作不可中断,要么全部完成,要么不执行(如赋值操作是原子的,但
i++不是)。 - 可见性:一个线程修改共享变量后,其他线程能立即看到新值。
- 有序性:程序执行顺序按代码顺序执行(但指令重排序可能破坏有序性)。
3. volatile的作用
- 保证可见性:强制将修改的值立即写入主存,并使其他线程的缓存失效,确保读取最新值。
- 禁止指令重排序:通过内存屏障(
lock前缀指令)确保操作顺序性,避免重排序引发的并发问题。
4. volatile的局限性
- 不保证原子性:例如
volatile int i; i++不是原子操作(需结合synchronized、Lock或AtomicInteger)。 - 使用条件:对变量的写操作不依赖当前值(如状态标记量),且不参与其他变量的不变式。
5. 适用场景
- 状态标记量:如
volatile boolean flag,控制线程执行流程。
双重检查锁(Double-Checked Locking):用于单例模式,避免指令重排序导致未初始化对象被访问。public class Singleton {private volatile static Singleton instance;public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton(); // 避免重排序}}}return instance;} }
6. 实现原理
通过JVM的lock指令实现内存屏障,功能包括:
- 阻止指令重排序跨越屏障;
- 强制缓存修改立即写入主存;
- 使其他CPU的缓存行无效。
关键对比
volatilevssynchronized:volatile仅保证可见性和有序性,不保证原子性;synchronized能保证三者,但性能开销较大。- 替代方案:原子类(如
AtomicInteger)通过CAS操作保证原子性。
总之,volatile是轻量级的同步机制,适用于不依赖当前值的状态标记或禁止重排序的场景,但需谨慎评估其原子性需求。
