【设计模式】单例模式(Singleton Pattern)
1. 定义
单例模式是创建型模式
的一种。此模式确保了一个类只有一个对象,可以直接访问,不需要进行实例化
2. 为什么我们需要单例模式
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
使用场景:
1、要求生产唯一序列号。
2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
3. 优缺点
优点
:控制实例数目,节省系统资源(对象的创建和销毁都会占用一定的资源)。
缺点
:没有接口,不能继承
4. 实现方法
4.1
- 线程安全
- 非lazy initialization
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
4.2
- 线程不安全
- lazy initialization
public class Singleton{
private static Singleton instance;
private Singleton(){}
private static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
4.3
- 线程安全
- lazy initialization
这种方法在前者的基础上使用sychronized
加锁来保证线程安全,但是影响的效率。
public class Singleton{
private static Singleton instance;
private Singleton(){}
private static sychronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
4.4
- 线程安全
- 非lazy initialization
线程安全,但是可能产生垃圾对象,浪费资源
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
4.5 Double checked locking
- 线程安全
- lazy initialization
public class Signleton{
//volatile保证当变量值改变时立即写入主存,并且通知其他线程其工作内存中的instance无效,保证并发的可见性。
private volatile static Singleton instance;
private Signleton(){}
public static Singleton getInstance(){
if(instance == null){
//volatile保证了可见性,但是不保证原子性。所以这里通过加锁的方式来保证其原子性。
sychronized(Signleton.class){
if(instance == null){
instance = Singleton ();
}
}
}
return instance;
}
}
4.6 静态内部类
- 线程安全(静态类只会被初始化一次)
- lazy initialization
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
4.7 枚举
这种方式也是《effective java》作者推荐的方式,其自动支持序列化机制。而且因为反射可以调用私有构造器,在不对构造函数增加判断的情况下,枚举可以完全保证对象的唯一。应为JVM规定所有枚举变量都是唯一的。
- 线程安全
- 非lazy initialization
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~