5.1 KiB
5.1 KiB
单例模式
目录
单例模式概念
定义
// 单例模式:确保一个类只有一个实例
// 提供全局访问点
// 适用于需要唯一实例的场景
使用场景
// 1. 配置管理类
// 2. 日志管理类
// 3. 数据库连接池
// 4. 线程池
// 5. 缓存管理
饿汉式
实现方式
// 饿汉式:类加载时就创建实例
public class Singleton {
// 私有静态实例
private static Singleton instance = new Singleton();
// 私有构造函数
private Singleton() {
}
// 公共静态方法
public static Singleton getInstance() {
return instance;
}
}
特点
// 优点:
// 1. 线程安全
// 2. 实现简单
// 缺点:
// 1. 类加载时就创建,可能浪费内存
// 2. 如果不需要使用,也会创建实例
懒汉式
实现方式
// 懒汉式:第一次使用时创建实例
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
线程安全问题
// ❌ 问题:多线程不安全
// 多个线程可能同时创建多个实例
// ✅ 解决:加锁
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// 缺点:每次调用都要加锁,性能差
双重检查锁定
实现方式
// 双重检查锁定(DCL)
public class Singleton {
private volatile static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
}
关键点
// 1. 两次检查:减少锁竞争
// 2. volatile:保证可见性,防止指令重排序
// 3. synchronized:保证线程安全
静态内部类
实现方式
// 静态内部类:推荐方式
public class Singleton {
private Singleton() {
}
// 静态内部类
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
特点
// 优点:
// 1. 线程安全
// 2. 懒加载:第一次调用时才创建
// 3. 性能好:不需要加锁
// 4. 实现简单
// 原理:
// 类加载时,静态内部类不会加载
// 只有调用 getInstance() 时,才会加载静态内部类
// JVM 保证类加载的线程安全
枚举单例
实现方式
// 枚举单例:最推荐的方式
public enum Singleton {
INSTANCE;
public void doSomething() {
// 方法实现
}
}
// 使用
Singleton.INSTANCE.doSomething();
特点
// 优点:
// 1. 线程安全
// 2. 防止反序列化创建新实例
// 3. 防止反射创建新实例
// 4. 实现简单
// 缺点:
// 1. 不能延迟加载
// 2. 枚举类不能继承其他类
单例模式最佳实践
1. 选择合适的方式
// 简单场景:饿汉式
// 需要懒加载:静态内部类
// 需要防止反射和反序列化:枚举单例
2. 注意线程安全
// 多线程环境必须保证线程安全
// 使用 volatile 和 synchronized
// 或使用静态内部类、枚举
3. 避免内存泄漏
// 单例持有 Context 时,使用 Application Context
private static Context sContext = context.getApplicationContext();
面试常见问题
Q1: 单例模式的实现方式?
答案:
- 饿汉式:类加载时创建,线程安全
- 懒汉式:使用时创建,需要加锁
- 双重检查锁定:两次检查,使用 volatile
- 静态内部类:推荐,懒加载,线程安全
- 枚举单例:最推荐,防止反射和反序列化
Q2: 双重检查锁定为什么需要 volatile?
答案:
- 防止指令重排序
- 保证可见性
- 确保多线程环境下正确初始化
Q3: 静态内部类为什么是线程安全的?
答案:
- JVM 保证类加载的线程安全
- 静态内部类在第一次调用时才加载
- 类加载时创建实例,天然线程安全
Q4: 枚举单例的优势?
答案:
- 线程安全
- 防止反序列化创建新实例
- 防止反射创建新实例
- 实现简单
最后更新:2024年