类锁通过锁定singleton.class确保单例创建的线程安全,但全方法同步会导致性能瓶颈;双重检查锁定(dcl)结合volatile可优化读写效率并防止指令重排;饿汉式、静态内部类和枚举单例是更优的无锁替代方案。

类锁(即 synchronized 修饰静态方法或以 Class 对象为锁的同步块)在单例模式中能有效保障线程安全,但效果取决于具体实现方式——它能阻止多线程重复初始化实例,但若设计不当,仍可能带来性能瓶颈或隐藏风险。
类锁如何作用于单例创建过程
当使用 synchronized 修饰静态 getInstance() 方法,或在同步块中以 Singleton.class 为锁对象时,所有线程必须竞争同一把“类级别”的锁。由于每个类在 JVM 中只有一个 Class 对象,这确保了临界区(如判断 + 实例化)的互斥执行。
- 锁粒度是整个类,不是实例,因此天然适用于静态资源控制
- 多个线程调用
getInstance()时,仅一个能进入初始化逻辑,其余阻塞等待 - 一旦实例创建完成,后续调用仍需获取锁——这是低效的根本原因
单纯类锁的典型缺陷
只靠类锁(如全方法加 synchronized)虽能保证安全,但牺牲了不必要的性能:
- 首次初始化后,所有读操作仍被串行化,无法并发访问已存在的实例
- 高并发场景下易形成线程争抢和上下文切换开销
- 未解决指令重排序问题:JVM 可能将
new Singleton()拆分为“分配内存→设置引用→执行构造器”,导致其他线程拿到未完全初始化的对象
类锁 + volatile 的协同优化
双重检查锁定(DCL)正是类锁与 volatile 配合的典型实践:
- 外层空检查不加锁,避免绝大多数读请求进入同步区
- 内层同步块用
Singleton.class锁住创建逻辑,确保唯一性 -
volatile修饰实例变量,禁止指令重排,并保证可见性——使构造完成后的引用对所有线程立即可见
比类锁更优的替代方案
并非所有场景都需显式使用类锁。以下方式在保障线程安全的同时更轻量、更可靠:
-
饿汉式:依赖类加载机制,在
static字段初始化时完成实例创建,无锁且线程安全 - 静态内部类:利用类加载的懒加载特性,由 JVM 保证初始化线程安全,无同步开销
- 枚举单例:JVM 保证枚举实例的创建是原子且线程安全的,还能天然防止反射和反序列化破坏
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











