单例模式(Singleton Pattern)
一、概念
单例模式的核心是保证一个单例对象的全局唯一性,并且提供给外部一个访问该唯一对象的接口。
-
解决问题:避免一个全局使用的类频繁地创建与销毁
-
如何解决:判断是否已经有这个单例,如果有则返回,如果没有则创建。
二、参与者
Singleton 单例类,负责创建自己的唯一实例,并提供外部访问接口。
Client 用户
三、代码例子
单例类(Singleton )——懒汉式
单例类(Singleton )——饿汉式
四、UML图
五、单例的实现方式
(零)不同实现方式总结
实现方式 | 优缺点 |
饿汉式 | 线程安全,调用效率高 ,但是不能延迟加载 |
懒汉式 | 线程安全,调用效率不高,能延迟加载 |
双检锁/双重校验锁式(DCL,即 double-checked locking) | 由于JVM底层内部模型原因,偶尔会出问题。不建议使用 |
登记式/静态内部类式 | 线程安全,资源利用率高,可以延时加载 |
枚举单例式 | 线程安全,调用效率高,但是不能延迟加载 |
(一)饿汉式
就是类加载的时候立即实例化对象
,实现的步骤是先私有化构造方法,对外提供唯一的静态入口方法
饿汉式通过classloader 机制
保证在类加载(static变量会在类装载时初始化
)的时候就立即初始化对象,此时也不会涉及多个线程对象访问该对象的问题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题
。因此,可以省略synchronized
关键字
1.优点
没有加锁,执行效率高,性能比懒汉式更好
2.缺点
类加载的时候就初始化,不管用与不用都会占用空间,浪费内存
(二)懒汉式
被外部调用 getInstance()
的时候才会实例化对象。从而实现了延迟加载
,但因为在方法上添加了synchronized关键字
上锁,每次调用getInstance方法都会同步
,所以对性能
的影响比较大
(三)双重校验锁式
(四)静态内部类式
(五)枚举单例式
(六)饿汉式和懒汉式区别
饿汉就是类一旦加载,就把单例初始化完成,保证Instance的时候,单例是已经存在的了,而懒汉只有当调用Instance的时候,才回去初始化这个单例。
另外从以下两点再区分以下这两种方式:
1、线程安全:
饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题。 懒汉式本身是非线程安全的,为了实现线程安全有几种写法,本文中只提到了比较基础的一种方法。
2、资源加载和性能:
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。
而懒汉式会延迟加载,在首次使用该类时才实例化对象,在第一次调用时要做初始化,性能上会有些延迟。
六、优缺点
(一)优点
- 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
- 避免对资源的多重占用(比如写文件操作)。
- 对唯一实例的访问控制,因为单例对象是全局唯一的,所以可以严格控制对象怎么访问及何时访问它;
(二)缺点
- 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了