设计模式-单例模式
第一篇博客,开通博客的目的一个是进行知识的分享,将自己的所学进行整理,并将整理好的结果分享出来;另一方面是养成一个好的习惯,可以通过博客当作自己的一个public记事本。最近在学设计模式相关的知识,就从设计模式开始学习整理把。由于刚步入工作,对设计模式不是特别的了解。在大学的项目中,用的最多的是MVC设计模式,其他的设计模式基本上就没怎么用过,就连普通的接口都用的少。进入工作后,发现很多地方都涉及到设计模式相关的知识点,所以觉得设计模式十分的重要。为了克服懒散,通过博客来进行自己的学习效果,进而督促自己来进行学习。对设计模式的学习是以《Head First 设计模式》为主,通过也参考着菜鸟教程进行学习。废话到此为止,开始整理。
简单描述:独一无二的对象
所属类型:创建型模式
适用情况:某个全局使用的类被频繁的创建和销毁,也就是对于那些我们只需要一个的对象
适用对象:线程池,缓存,对话框,处理偏好设置和注册表的对象,日志对象,打印机,显卡等设备的驱动对象
实现原则:确保一个类只有一个实例,并提供全局访问点。换句话来说就是:单例模式没有公有构造器,也就是无法在该类的外面new出来该类的实例。
实现方法:
利用一个私有静态变量来记录单例类的唯一实例
将构造器声明为私有,也就是只有在类的内部才能调用构造器,进而对它进行实例化
通过一个公有的静态方法来进行实例化,并将之前创造出来用来记录唯一实例的对象进行返回
接下来对单例模式的几种实现方法进行整理。
1. 懒汉式,线程不安全
所谓的懒汉式,从中提取出关键词就是“懒”。怎样才能实现“懒”呢?这里的懒其是就是为了实现懒加载。换句话来说就是"如果我不需要这个实例,我就不去给他实例化"
。
先上代码吧:
public class Singleton { //创建一个私有变量用来保存单例模式的唯一实例 private static Singleton mInstance; //将构造声明为私有 private Singleton() { } //通过公有的静态方法对其进行实例化 public static Singleton getInstance() { if (mInstance == null) { mInstance = new Singleton(); } return mInstance; } }
就这样,简单的一个单例模式就完成了。如果不考虑线程安全,也就是不需要考虑多线程的情况,那么这样子的单例用起来没问题。如果考虑到多线程的情况,就会存在一些问题。下面再对这种单例模式进行简单总结,然后进行单例模式的其他形式的整理。
是否实现懒加载 | 多线程是否安全 | 实现难易程度 |
---|---|---|
是 | 否 | 易 |
2. 懒汉式,线程安全
上面讲到了一个单例模式的简单实现,也提到了那是一种线程不安全的做法。下面就要对上面的情况进行修改,然后让他达到线程安全。
还是先上代码吧:
public class Singleton { //创建一个私有变量用来保存单例模式的唯一实例 private static Singleton mInstance; //将构造声明为私有 private Singleton() { } //通过公有的静态方法对其进行实例化 public static synchronized Singleton getInstance() { if (mInstance == null) { mInstance = new Singleton(); } return mInstance; } }
和线程不安全的代码相比较,变化只有一处,就是添加了synchronized
关键字。添加了synchronized
关键字,就可以保证在多线程情况下是线程安全的。但这样同时也带来了一个问题,如果每次使用这个单例对象的时候都要进行一次同步,频繁的进行访问,那将会十分的耗时。所以这种方式适合于对该方法使用不是特别频繁的情况。也对这种情况进行简单小结。
是否实现懒加载 | 多线程是否安全 | 实现难易程度 |
---|---|---|
是 | 是 | 易 |
3. 饿汉式
从多线程的角度来进行考虑,懒汉式的缺点是一旦对其使用的频率过高,效率就成了问题,接下来从效率的角度来进行考虑。
还是一样,先上代码:
public class Singleton { //类加载时进行初始化 private static Singleton mInstance = new Singleton(); //将构造声明为私有 private Singleton() { } //调用静态方法时将该对象返回 public static Singleton getInstance() { return mInstance; } }
是否实现懒加载 | 多线程是否安全 | 实现难易程度 |
---|---|---|
否 | 是 | 易 |
4. 双重检查加锁
从名字上来看,我们可能要进行两次什么操作了。先上代码吧:
public class Singleton { //创建一个私有变量用来保存对象 private volatile static Singleton mInstance; //构造方法私有化 private Singleton() { } //根据条件来对mInstance进行实例化 public static Singleton getInstance() { if (mInstance == null) { synchronized (Singleton.class) { if (mInstance == null) { mInstance = new Singleton(); } } } return mInstance; } }
和懒汉式的代码进行比较,发现多了个volatile
关键字,这个关键字在这的作用主要是保持内存可见性
,换句话来说就是:在一个线程对mInstance的值进行了修改,在另一个线程就能马上看到
。接着对mInstance
进行实例化的步骤中,可以看到多了一层判断,这样一方面能够保证线程的安全,另一方面能够保持比较高的性能。双重检查加锁的模式在JDK1.5之后才开始使用,在JDK1.5之前许多JVM对于volatile
关键字的实现会导双重检查加锁的失效。最后再对这种单例模式进行简单总结。
是否实现懒加载 | 多线程是否安全 | 实现难易程度 |
---|---|---|
是 | 是 | 较复杂 |
5. 小结
最后再来整理一下单例模式的几个要点:
- 单例模式确保程序中一个类最多只有一个实例
- 单例模式提供访问这个实例的全局点
- 在Java中实现单例模式需要私有的构造器、一个静态方法和一个静态变量
- 确定在性能和资源上的限制,然后小心地选择适当的方案来实现单例,以解决多线程的问题
- 如果不是使用JDK1.5之后的版本,双重检查加锁实现会失效
- 如果使用多个类加载器,可能导致单例失效而产生多个实例
- 如果使用JVM1.2或者是之前的版本,就必须建立单例注册表,以免垃圾回收器将单例回收
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!