java单例线程安全核心是防止多线程重复初始化,需用类锁(如synchronized(singleton.class))或双重检查锁定(dcl)+volatile;禁用synchronized(this),因实例未创建时this不存在;还需防御反射和反序列化破坏。

Java 中保证单例对象初始化线程安全,核心是防止多个线程同时执行构造逻辑、重复创建实例。synchronized 可以用,但必须用对地方——不能修饰构造方法(语法禁止),也不能盲目加在普通实例方法上;真正起作用的是类锁(Singleton.class)或静态同步控制。
用 synchronized 保护静态获取方法(基础可行)
最直白的方式是把 getInstance() 声明为 static synchronized:
public class Singleton {
private static Singleton instance;
private Singleton() {} // 私有构造,防外部 new
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 这等价于在方法体外加
synchronized(Singleton.class) - 所有线程调用
getInstance()时竞争同一把类锁,确保初始化只发生一次 - 缺点:每次调用都加锁,高并发下性能明显下降
推荐做法:双重检查锁定(DCL)+ volatile
兼顾安全与性能的主流方案:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查(无锁)
synchronized (Singleton.class) { // 加类锁
if (instance == null) { // 第二次检查(锁内)
instance = new Singleton(); // 关键:volatile 防止指令重排序
}
}
}
return instance;
}
}
-
volatile确保instance的写操作对其他线程立即可见,且禁止 JVM 将构造过程重排序为“分配内存 → 设置引用 → 初始化字段” - 两次判空避免多线程同时通过第一层检查后重复创建
- 锁只在真正需要初始化时才进入,后续调用完全无锁
为什么不能用 synchronized(this) 或实例锁?
- 单例还没创建出来,
this根本不存在,无法作为锁对象 - 即使你绕过编译(比如在工厂方法里先 new 再锁),此时锁的是新实例,而其他线程还在争抢
getInstance(),完全起不到互斥初始化的作用
补充防护(生产环境建议)
仅靠 synchronized + volatile 还不够健壮,还需应对:
-
反射攻击:在私有构造中加非空检查
private Singleton() { if (instance != null) { throw new RuntimeException("Singleton already initialized"); } } -
反序列化破坏:添加
readResolve()方法private Object readResolve() { return getInstance(); }
不复杂但容易忽略细节
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










