volatile关键字的主要作用是保证可见性和禁止指令重排序,确保多线程环境下变量的修改能立即对其他线程可见,并通过happens-before规则提供线程安全保障。 以下是具体说明:
1. 保证可见性- 主内存同步机制:被volatile修饰的变量在修改后会立即写入主内存,其他线程读取时直接从主内存获取最新值,而非线程本地缓存(工作内存)。
- 示例场景:volatile boolean flag = false;// 线程A修改flagflag = true; // 线程B读取flag时,保证看到最新值true若未使用volatile,线程B可能因缓存未更新而读取到旧值false。
2. 禁止指令重排序- 编译器/处理器优化限制:JVM和CPU可能对指令重排序以提高性能,但volatile会通过内存屏障(Memory Barrier)禁止特定重排序,确保代码执行顺序符合预期。
- 双重检查锁定(DCL)中的应用:class Singleton { private static volatile Singleton instance; public static Singleton getInstance() { if (instance == null) { // 第一次检查 synchronized (Singleton.class) { if (instance == null) { // 第二次检查 instance = new Singleton(); // 非原子操作(分配内存→初始化→赋值) } } } return instance; }}volatile防止instance = new Singleton()被重排序为先赋值后初始化,避免其他线程读取到未初始化的对象。
3. happens-before规则4. 与CAS结合保证原子性- 原子类实现基础:java.util.concurrent.atomic包下的类(如AtomicInteger)通过volatile变量+CAS(Compare-And-Swap)实现原子操作。
- CAS原理:// AtomicInteger示例public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}volatile保证可见性,CAS保证原子性,二者结合实现无锁并发。
5. 适用场景6. 与synchronized的区别- 可见性:二者均保证可见性,但volatile仅修饰变量,synchronized修饰代码块/方法。
- 原子性:synchronized保证复合操作原子性,volatile仅保证单次操作。
- 性能:volatile开销更低,但适用场景有限。
总结volatile通过可见性、禁止重排序和happens-before规则,为多线程编程提供了轻量级同步机制,常用于状态标志、双重检查锁定等场景。但需注意其局限性,复杂操作仍需结合synchronized或原子类实现。