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"(观测时消失,不观测时出现)。

相关推荐
rannn_1112 小时前
【力扣hot100】链表专题|160、206、234、141、142
java·算法·leetcode·链表·面试·开发
IKUN家族4 小时前
Spring IoC&DI
java·spring·rpc
山荷枝5 小时前
03-框架--Spring
java·后端·spring
ChaHae-In6 小时前
SpringBoot统一功能处理
java·spring boot·后端
coder!mq6 小时前
说几个常见的语法糖?
java·开发语言·算法
gugucoding6 小时前
38. 【Java】Stream API(下):高级操作与性能
java·开发语言
心平气和量大福大7 小时前
android-实例-蒲公英-更新与安装-5-签名与生成APK
android·java·开发语言
952367 小时前
Spring-cloud配置中心
java·spring·spring cloud·微服务
ydd1001008 小时前
寻找两个正序数组的中位数 Java 题解,二分分割详解
java·开发语言
真上帝的左手9 小时前
10. 软件设计&架构-Spring Security 7 整合CAS SSO 单点登录
java·spring·架构·sso