synchronized 在双重检查锁(dcl)中仅锁定创建实例的临界区,提升性能;需配合两次 null 检查和 volatile 修饰,防止重排序导致的半初始化问题。

在 Java 单例模式中,synchronized 本身不直接“实现”双重检查锁,而是配合 if 判断和 volatile 关键字,共同构成双重检查锁定(DCL)机制。它的核心作用是:**只对真正需要创建实例的临界区加锁,而非整个方法,兼顾线程安全与性能。**
第一次检查:锁外判断,避免无谓加锁
调用 getInstance() 时,先检查单例引用是否为 null:
- 若已存在(非
null),直接返回,完全不进入同步块,零锁开销 - 只有为
null时,才准备加锁——这是性能优化的关键一步
第二次检查:锁内确认,防止重复创建
多个线程可能同时通过第一次检查(比如都看到 null),然后争抢同一把锁。获得锁的线程进入同步块后,必须再次检查:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 因为可能已有其他线程抢先完成创建并赋值,此时
instance已非null - 只有再次确认为
null,才执行new Singleton();否则直接返回已创建好的实例
synchronized 的具体写法与对象选择
推荐使用类对象作为锁目标:
-
synchronized (Singleton.class)—— 锁的是当前类的Class对象,全局唯一、稳定可靠 - 不能用
this或实例对象,因为getInstance()是静态方法,尚未有实例 - 也不建议新建任意对象(如
new Object())作锁,易造成锁粒度混乱或内存浪费
volatile 不可省略,否则 synchronized 也救不了
即使两次 if 都写对,若缺少 volatile 修饰单例引用:
- JVM 可能将
new Singleton()拆解为“分配内存→赋值引用→调用构造器”,并重排序为“分配内存→赋值引用→调用构造器” - 导致其他线程在同步块外就看到非
null的引用,但对象尚未初始化完毕,引发 半初始化问题 -
volatile通过内存屏障禁止该重排序,并保证写操作对所有线程立即可见
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










