volatile 的作用

在上篇博客中,已经提到 volatile 保证内存可见性的作用,这次加上 volatile 禁止指令重排序的作用。

java 复制代码
class SingletonLazy {
    private static volatile SingletonLazy instance = null;
    
    public static  SingletonLazy getInstance() {
        if(instance == null) {
            synchronized (SingletonLazy.class) {
                if(instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
    
    private SingletonLazy() {
        
    }
}

如果没有 volatile,这段代码在多线程环境下可能返回一个"未完全初始化"的对象,导致严重 bug!

🔍 为什么需要它?------ 深入分析"对象创建"过程

new SingletonLazy() 看似一行代码,但在 JVM 底层其实分为 3 步:

  1. 分配内存空间(为 SingletonLazy 对象分配堆内存)

  2. 调用构造方法(初始化对象的字段、执行构造逻辑)

  3. 将引用赋值给 instanceinstance = 引用地址

⚠️ 问题:JVM 可能重排序!

在没有 volatile 的情况下,JVM 或 CPU 可能将步骤 2 和 3 重排序,变成:

  1. 分配内存

  2. 将引用赋值给 instance(此时对象尚未初始化!)

  3. 调用构造方法(延迟执行)

💥 这就是 "指令重排序"(Instruction Reordering) 优化。

🧨 多线程下的灾难场景(无 volatile

假设有两个线程 T1 和 T2:

时间 T1(正在创建实例) T2(调用 getInstance()
1 执行 new: ① 分配内存 ③ 先赋值 instance = 地址(重排序!) ---
2 --- 检查 if (instance == null)false!(因为 instance 已非 null)
3 --- 直接 return instance(拿到一个未初始化的对象!)
4 ② 执行构造方法(太晚了!) 使用 instance访问未初始化的字段 → NullPointerException / 数据错误!

❌ 这就是著名的 "DCL 失效问题"(在 Java 5 之前无法安全修复)。

volatile 如何解决这个问题?

当你声明:

java 复制代码
private static volatile SingletonLazy instance = null;

volatile 提供两个关键保障:

  1. 禁止重排序
  • JVM 保证 instance = new SingletonLazy() 的三步操作不会被重排序。

  • 必须严格按照:分配内存 → 初始化 → 赋值 的顺序执行。

  1. 保证可见性
  • 当 T1 执行 instance = ... 后,T2 立即能看到最新值(不会读到缓存中的 null)。

✅ 结果:T2 要么看到 instance == null(继续等待锁),

要么看到一个完全初始化好的对象。

❌ 如果去掉 volatile 会怎样?

java 复制代码
private static SingletonLazy instance = null; // 没有 volatile!
  • 单线程:一切正常。

  • 多线程:有极小概率(但确实存在)返回未初始化对象。

  • Bug 难以复现(依赖 CPU/JVM/时机),但一旦发生,后果严重(崩溃、数据错乱)。

🐞 这类 bug 被称为 "Heisenbug"(观测时消失,不观测时出现)。

相关推荐
吃饱了得干活11 小时前
Spring Cloud Gateway 微服务网关:路由、断言、过滤器
java·spring cloud
lwx5728012 小时前
探秘InnoDB:搞懂它的内存、线程、磁盘与日志刷盘策略
java·后端
Flynt14 小时前
从Spring Boot 4.0升到4.1,我在Maven和gRPC上栽了跟头
java·spring boot·后端
plainGeekDev15 小时前
Activity 间传值 → Navigation 参数
android·java·kotlin
plainGeekDev15 小时前
onActivityResult → ActivityResult API
android·java·kotlin
Sunia15 小时前
《AgentX 专栏》10-生产部署:3台2C4G云服务器把企业级Agent真正跑起来的完整方案
java·架构
ZhengEnCi16 小时前
J7A-高级Java工程师面试三道灵魂拷问-深度广度与工程素养的终极检验
java·后端
狼爷1 天前
吃透 Java Function 接口,搞定 99% 的 Stream 场景
java·函数式编程
祎雪双十Gy2 天前
从 DataX 的配置加载说起:我用 FastJson2 做了一个轻量级动态配置管理库
java·后端
小锋java12342 天前
分享一套锋哥原创的SpringBoot4+Vue3宠物领养网站系统
java