synchronized实现线程安全单例需用singleton.class类锁,因静态方法无this;基础写法为static synchronized getinstance(),dcl优化需volatile+双重判空;还需防御反射、反序列化及异常安全。

用 synchronized 实现线程安全的单例,核心是控制静态实例的初始化过程,确保多线程环境下只创建一次对象。最直接有效的方式是配合类锁(Singleton.class)使用,而不是依赖对象锁——因为单例实例尚未生成,没有 this 可用。
基础写法:静态同步方法
这是最简单、零配置的实现:
- 将
getInstance()声明为public static synchronized - 内部判空后创建实例,JVM 保证该方法同一时刻仅被一个线程执行
- 私有构造函数防止外部 new,是单例成立的前提
优点是逻辑清晰、天然线程安全;缺点是每次调用都加锁,高并发下性能损耗明显。
优化写法:双重检查锁定(DCL)
兼顾安全性与性能,主流推荐方案:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 第一层非同步判空:已有实例则快速返回,不进锁
- 第二层在
synchronized(Singleton.class)块内再次判空,防止多个线程同时通过第一层后重复创建 -
instance字段必须用volatile修饰,禁止指令重排序,避免其他线程看到半初始化对象
这种写法把同步开销限制在首次创建阶段,后续调用完全无锁。
不能忽略的关键防御点
仅靠 synchronized 和 volatile 还不够,需应对常见攻击手段:
- 反射攻击:在私有构造函数中增加
if (instance != null) throw new RuntimeException("Singleton already initialized") - 反序列化攻击:添加
private Object readResolve() { return instance; },强制返回已有实例 - 异常安全:
getInstance()中避免抛出未捕获异常,否则可能留下instance == null但初始化失败的状态
为什么必须用类锁,而不是 this 锁
因为 getInstance() 是静态方法,没有实例对象可绑定;Singleton.class 天然存在、全局唯一,所有线程竞争同一把锁,才能真正串行化静态初始化逻辑。若误用实例锁,编译会报错,运行也无意义。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










