一、单例模式是什么?
单例模式(Singleton) 是创建型设计模式 。 核心定义 : 保证一个类在整个程序运行中,有且仅有一个实例对象,并提供一个全局访问入口。
二、单例模式三大核心特点(必背)
- 私有构造方法:禁止外部 new 对象
- 私有静态实例:类内部唯一对象
- 公共静态方法:全局唯一获取实例入口
三、单例解决什么问题?
- 避免重复创建对象,节省内存
- 全局统一资源管控(连接池、配置类、工具类)
- 保证操作唯一性(日志、计数器、线程池)
四、两大分类
- 饿汉式 :类加载时直接创建实例 → 无懒加载、线程安全
- 懒汉式 :使用时才创建实例 → 懒加载、省内存、需要解决线程安全
五、八种实现方式
1. 饿汉式(静态常量)【可用、简单】
特点 :类加载直接初始化,天生线程安全
java
public class Singleton {
// 1. 静态常量实例
private static final Singleton instance = new Singleton();
// 2. 私有构造
private Singleton(){}
// 3. 全局获取
public static Singleton getInstance(){
return instance;
}
}
✅ 优点:简单、无锁、线程绝对安全、性能高 ❌ 缺点:无懒加载,类加载就创建,不用也占内存
2. 饿汉式(静态代码块)
适合:需要初始化额外资源
java
public class Singleton {
private static Singleton instance;
static {
instance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
3. 懒汉式(线程不安全)
特点:用时才创建,多线程会创建多个实例
java
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
❌ 致命问题:多线程并发会创建多个对象
4. 懒汉式(同步方法)
java
public class Singleton {
private static Singleton instance;
private Singleton(){}
// 加锁
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
✅ 线程安全 ❌ 每次获取都加锁,高并发性能极差
5. 懒汉式(同步代码块)
java
// 错误!不能保证单例
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
instance = new Singleton();
}
}
return instance;
}
❌ 双重检查缺失第一层锁判断,多线程依旧会重复创建
6. 双重检查锁 DCL
终极优化:懒加载 + 线程安全 + 高性能
java
public class Singleton {
// volatile 禁止指令重排!核心!
private static volatile Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
// 第一次判断:避免每次加锁
if(instance == null){
synchronized (Singleton.class){
// 第二次判断:防止多线程穿透
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
为什么必须加 volatile?(面试必考)
new 对象 底层三步:
- 开辟内存空间
- 初始化对象
- 赋值给 instance
CPU 指令重排 可能变成:1→3→2 导致其他线程拿到 半初始化对象,程序报错。
volatile 作用:禁止指令重排、保证可见性
✅ 优点:懒加载、线程安全、并发高性能 ❌ 写法稍复杂、需要理解 volatile
7. 静态内部类单例
java
public class Singleton {
// 私有构造
private Singleton(){}
// 静态内部类:外部类加载不会加载内部类
private static class Inner{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return Inner.INSTANCE;
}
}
原理(满分解释)
- 外部类加载不加载内部类 → 实现懒加载
- 内部类静态变量初始化由 JVM 保证线程安全
- 调用
getInstance()才加载内部类,创建唯一实例
✅ 懒加载 ✅ 无锁、性能极高 ✅ JVM 天然线程安全 🔥 面试最推荐、代码简洁、无坑
❌ 缺点:无法防止反射破坏单例
8. 枚举单例
java
public enum Singleton {
INSTANCE;
// 自定义方法
public void doSomething(){
System.out.println("业务方法");
}
}
使用:
java
Singleton.INSTANCE.doSomething();
最强优点
- 绝对线程安全
- 杜绝反射破坏、序列化破坏
- 写法极简
- 天然单例、不会多创建
✅ Java 官方公认最完美单例 ❌ 不支持懒加载(类加载直接初始化)
六、四大破坏单例的方式 & 防护
1. 反射破坏
通过暴力反射创建新对象,除了枚举 全部可破。 枚举天然防反射
2. 序列化破坏
对象序列化再反序列化会生成新对象。 枚举天然防序列化。
3. 克隆破坏
重写 clone 方法会产生新对象。
4. 多线程并发
普通懒汉式无锁直接失效。
七、八种单例终极对比表(背诵版)
表格
| 方式 | 懒加载 | 线程安全 | 性能 | 防反射 | 推荐度 |
|---|---|---|---|---|---|
| 饿汉静态常量 | ❌ | ✅ | 极高 | ❌ | ⭐⭐⭐ |
| 饿汉静态代码块 | ❌ | ✅ | 极高 | ❌ | ⭐⭐⭐ |
| 普通懒汉 | ✅ | ❌ | 高 | ❌ | ❌废弃 |
| 同步方法懒汉 | ✅ | ✅ | 极低 | ❌ | ⭐⭐ |
| 同步代码块懒汉 | ✅ | ❌ | 高 | ❌ | ❌错误 |
| DCL 双重检查 | ✅ | ✅ | 极高 | ❌ | ⭐⭐⭐⭐ |
| 静态内部类 | ✅ | ✅ | 极高 | ❌ | ⭐⭐⭐⭐⭐ |
| 枚举 | ❌ | ✅ | 极高 | ✅ | ⭐⭐⭐⭐⭐⭐ |
八、开发中到底用哪个?
- 需要懒加载、高性能、无漏洞 → 静态内部类
- 高并发、框架底层、绝对安全 → 枚举
- 简单工具类、无需懒加载 → 饿汉式
- 面试手写最优代码 → DCL / 静态内部类
九、单例模式真实业务场景
- Spring Bean 默认单例
- 数据库连接池
- Redis 连接工具类
- 全局配置类、系统参数
- 日志工具、计数器
- 线程池对象
- 系统缓存管理器
十、面试高频问答
1. DCL 为什么要 volatile?
防止 JVM 指令重排,避免线程获取半初始化对象,保证多线程安全。
2. 静态内部类原理?
利用外部类不加载内部类 实现懒加载,利用JVM 静态初始化线程安全保证单例。
3. 枚举为什么是最完美单例?
JVM 底层禁止反射枚举构造方法,天然杜绝反射、序列化、多线程问题。
4. 饿汉和懒汉区别?
饿汉:类加载创建,无懒加载、省锁、费内存 懒汉:用时创建,懒加载、省内存、需要控线程安全