👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中... 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云
文章目录
一、入门
什么是单例模式?
单例模式是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。它常用于需要全局唯一对象的场景,如配置管理、连接池等。
为什么要单例模式?
- 节省资源
- 场景:某些对象创建和销毁成本高,如数据库连接池、线程池等。
- 原因:单例模式确保这些资源只创建一次,避免重复创建和销毁,节省系统资源。
- 全局访问点
- 场景:需要在多个模块或组件中共享同一个对象,如配置管理器、日志记录器等。
- 原因:单例模式提供一个全局访问点,方便不同模块共享同一实例,避免频繁传递对象。
- 保持一致性
- 场景:需要确保某些操作或状态在整个应用中保持一致,如缓存管理、计数器等。
- 原因:单例模式确保只有一个实例,避免多个实例导致状态不一致。
- 控制实例数量
- 场景:某些情况下,限制实例数量是必要的,如打印机管理、文件系统等。
- 原因:单例模式确保只有一个实例存在,避免资源冲突或竞争条件。
- 简化设计
- 场景:某些设计模式(如工厂模式、抽象工厂模式)中,单例模式可以简化对象创建和管理。
- 原因:单例模式减少对象创建的复杂性,使设计更简洁。
如何实现单例模式?
- 私有构造函数:防止外部通过 new 创建实例。
- 静态私有实例:类内部持有唯一的实例。
- 静态公有方法 :提供全局访问点,返回唯一实例。
饿汉式
java
public class Singleton {
// 在类加载时就创建实例
private static final Singleton INSTANCE = new Singleton();
// 私有构造函数,防止外部通过 new 创建实例
private Singleton() {}
// 提供全局访问点
public static Singleton getInstance() {
return INSTANCE;
}
}
- 实例在类加载时立即创建。
- 无论是否使用,实例都会提前初始化。
特点
- 线程安全:实例在类加载时创建,由 JVM 保证线程安全。
- 提前加载:无论是否使用,实例都会在类加载时创建,可能会占用资源。
- 实现简单:代码简洁,无需考虑多线程问题。
懒汉式
java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 优点:线程安全。
- 缺点:每次调用都加锁,性能较差。
双重检查(DCL)
java
public class Singleton {
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;
}
}
- 优点:线程安全且性能较好。
- 缺点:实现稍复杂。
静态内部类
java
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
- 优点:线程安全,延迟加载,实现简单。
- 因为实例的创建由 JVM 在类加载时完成,且静态内部类的加载是线程安全的。
- 实例在第一次调用 getInstance() 时创建。
- 延迟加载(Lazy Initialization),只有在需要时才创建实例。
枚举
java
public enum Singleton {
INSTANCE;
public void doSomething() {
// 业务逻辑
}
}
- 优点:线程安全,防止反射攻击,简洁。
二、单例模式在框架源码中的运用
Java 标准库 RunTime类
Runtime 类 :Runtime
类是典型的单例模式实现,用于管理应用程序的运行环境。
java
public class Runtime {
private static final Runtime currentRuntime = new Runtime();
private Runtime() {}
public static Runtime getRuntime() {
return currentRuntime;
}
}
- 通过 Runtime.getRuntime() 获取唯一的
Runtime
实例。
Spring Framework
Spring 框架中的单例模式主要用于管理 Bean 的生命周期。
Spring 的默认 Bean 作用域:
- Spring 容器中的 Bean 默认是单例的(
Singleton
作用域),即每个 Spring 容器中只有一个实例。
java
@Service
public class UserService {
// UserService 在 Spring 容器中是单例的
}
@Service
@Scope("singleton") // 默认就是单例,可以省略
public class UserService {
}
单例 Bean 的管理:
- Spring 容器通过单例模式确保全局唯一的 Bean 实例,避免重复创建。
- 示例:Spring 的 ApplicationContext 本身就是单例的。
三、总结
单例模式的优点
- 全局唯一访问点
- 单例模式确保一个类只有一个实例,并提供一个全局访问点,方便其他模块或组件共享该实例。
- 示例:配置管理器、日志记录器等。
- 节省资源
- 对于创建成本高的对象(如数据库连接池、线程池),单例模式可以避免重复创建和销毁,节省系统资源。
- 示例:数据库连接池通常只需要一个实例来管理所有连接。
- 保持一致性
- 单例模式确保全局状态的一致性,避免多个实例导致状态冲突。
- 示例:缓存管理器需要确保缓存数据的一致性。
- 简化设计
- 单例模式可以减少对象创建的复杂性,使代码更简洁。
- 示例:Spring 框架中的单例 Bean 管理。
- 线程安全(某些实现)
- 通过正确的实现(如饿汉式、静态内部类、枚举),单例模式可以保证线程安全。
单例模式的缺点
- 难以扩展
- 单例模式通常通过私有构造函数限制实例化,导致难以扩展或修改。
- 如果需要多个实例或修改单例行为,可能需要重构代码。
- 隐藏依赖
- 单例模式通过全局访问点获取实例,可能导致代码的依赖关系不清晰,增加调试和维护难度。
- 测试困难
- 单例模式的全局状态可能导致单元测试困难,因为测试用例之间可能会相互影响。
- 示例:测试一个依赖单例类的模块时,可能需要重置单例状态。
- 可能滥用
- 单例模式容易被滥用,导致系统中出现大量全局状态,增加代码的耦合性。
- 生命周期管理问题
- 单例模式的生命周期通常与应用程序一致,可能导致资源无法及时释放。
- 示例:单例对象持有数据库连接,可能导致连接无法关闭。
单例模式的适用场景
- 全局唯一对象
- 需要全局唯一的对象时,可以使用单例模式。
- 示例:
- 配置管理器(
ConfigManager
)。 - 日志记录器(
Logger
)。 - 数据库连接池(
DataSource
)。
- 配置管理器(
- 资源密集型对象
- 对于创建成本高的对象,单例模式可以避免重复创建和销毁。
- 示例:
- 线程池(
ThreadPool
)。 - 缓存管理器(
CacheManager
)。
- 线程池(
- 共享状态管理
- 需要全局共享状态时,可以使用单例模式。
- 示例:
- 计数器(
Counter
)。 - 全局锁(
Lock
)。
- 计数器(
- 工具类
- 对于无状态的工具类,单例模式可以避免重复创建实例。
- 示例:
- 日期格式化工具(
DateUtils
)。 - 字符串处理工具(
StringUtils
)。
- 日期格式化工具(
- 框架核心组件
- 在框架中,单例模式常用于管理核心组件。
- 示例:
- Spring 的
ApplicationContext
。 - MyBatis 的
SqlSessionFactory
。
- Spring 的