volatile用于解决双重检查锁定中的指令重排序问题,确保instance的可见性和有序性,防止线程看到未初始化完成的对象;正确写法需static volatile修饰、私有构造、两次null检查及类对象同步。

在 Java 中,用 volatile 保护单例模式中的 instance 变量,主要是为了解决**双重检查锁定(Double-Checked Locking)** 下的指令重排序问题,确保多线程环境下单例对象的**可见性**和**有序性**,避免返回一个尚未初始化完成的对象。
为什么需要 volatile?
在没有 volatile 时,JVM 可能将如下初始化过程重排序:
1. 分配内存空间 2. 初始化对象(调用构造函数) 3. 将 instance 引用指向该内存地址
重排序后可能变成:1 → 3 → 2。此时另一个线程可能看到 instance != null,但对象实际还未初始化完毕,导致 NPE 或未定义行为。volatile 禁止这种重排序,并保证写操作对所有线程立即可见。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
正确的双重检查锁定写法
以下是线程安全、高性能的懒汉式单例实现:
立即学习“Java免费学习笔记(深入)”;
public class Singleton {
private static volatile Singleton instance;
<pre class="brush:php;toolbar:false;">private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查(无锁)
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查(加锁后)
instance = new Singleton(); // volatile 保证此处的可见性和有序性
}
}
}
return instance;
}}
关键细节说明
-
必须声明为
static volatile:仅volatile不够,必须是类级别的静态变量;仅static不够,无法防止重排序 -
构造函数私有:防止外部通过
new创建实例 - 两次 null 检查缺一不可:第一次减少同步开销,第二次防止重复初始化
- synchronized 锁的是类对象:确保全局唯一锁,避免不同实例锁错对象
其他安全替代方案(可选参考)
- 静态内部类(推荐):利用类加载机制保证线程安全,无需 volatile 或 synchronized
- 枚举单例:天然序列化安全、线程安全,且防止反射攻击
- 饿汉式:简单但不支持延迟加载

















