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

相关推荐
咩咩啃树皮5 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
鱟鲥鳚6 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
大模型码小白7 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司8 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地9 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
wuqingshun3141599 小时前
TCP超时重传机制是为了解决什么问题?
java
白露与泡影10 小时前
Arthas 实战指南:从方法耗时定位到 JVM 变量热修改
服务器·jvm·c#
莫逸风11 小时前
【AgentScope 2.0】 0. 学习指南
java·llm·agent·agentscope
z1234567898612 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
yaoxin52112313 小时前
470. Java 反射 - Member 接口与 AccessFlag
java·开发语言·python