秒懂设计模式——单例设计模式


(三)单例设计模式

1.先解释一下,什么是单例模式呢?

在Java中是这样定义的:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。

显然从单例模式的定义中,我们可以发现它有三个要点

①某个类只能有一个实例;

②它必须自行创建这个实例;

③它必须自行向整个系统提供这个实例。

2.要满足这三个要点,应该如何实现呢?下面让我们来逐条分析:

①如何保证某个类只能有一个实例?

让我先来想一下,一个类的对象是如何创建的呢?答案是:一个类的对象的产生是由类构造函数来完成。那么,我们是不是可以通过私有类构造函数来实现呢?

②如何保证类定义中含有一个该类的静态私有对象?

这个就很简单了,让单例类自己创建自己的唯一实例

③如何保证它自行向整个系统提供这个实例?

这个也不难,对外提供获取此对象的静态方法即可。

3.单例模式分为几种呢?

①懒汉式,线程不安全;

②懒汉式,线程安全;

③饿汉式;

④双检锁/双重校验锁(DCL,即 double-checked locking);

⑤登记式/静态内部类;

⑥枚举式;

下面仍然通过一个故事,逐一介绍这几种类型的单例模式,并且在最后,还会分析一下他们的性能差异。

【讲故事】某野鸡大学(),只有一个学生会主席(唯一对象,自行实例化),还是一个漂亮妹子叫“M蓉”,其他附近野鸭大学的学长都喜欢请她去自己宿舍,连夜补习英语(向整个系统提供实例)。

(1)懒汉式,线程不安全

先解释一下为什么叫懒汉式:“懒”顾名思义就是延迟,懒汉式就是指,只有在获取此单例对象时,才会去创建此对象

【Java代码】

①创建一个单例模式的类。

package com.liyan.lazy;
/*** 懒汉式的M蓉(线程不安全)* <p>Title: LazyMRong</p>  * @author  Liyan  * @date    2017年4月27日 下午2:00:44*/
public class LazyMRong {//1.私有空参构造,防止别人创建private LazyMRong(){}//2.自己创建自己的唯一实例private static LazyMRong lazyMRong ;//3.对外提供获取此对象的静态方法public static LazyMRong getLazyMRong() {if (lazyMRong == null) {//懒汉式意味着,只有你在想获取时,才创建此对象return new LazyMRong();}return lazyMRong;}public void teachEnglish() {System.out.println("连夜补习外语!");}
}

②创建外部访问

package com.liyan.lazy;
/*** S喆请M蓉补习外语* <p>Title: SZhe</p>  * @author  Liyan  * @date    2017年4月27日 下午2:15:23*/
public class SZhe {public static void main(String[] args) {//通过静态方法,获取到单例对象LazyMRong lazyMRong = LazyMRong.getLazyMRong();//调用补习外语方法lazyMRong.teachEnglish();}
}

分析:首先懒汉式肯定是延迟初始化的,但是不支持多线程。因为没有加锁synchronized,所以有些资料上认为,从严格意义上讲,它并不算单例模式

(2)懒汉式,线程安全

这个跟上面的懒汉式,线程不安全,唯一的区别就是:在获取单例类的静态方法上,加上了synchronized关键字进行修饰

package com.liyan.lazy;
/*** 懒汉式的M蓉(线程安全)* <p>Title: LazyMRong</p>  * @author  Liyan  * @date    2017年4月27日 下午2:00:44*/
public class LazyMRong {//1.私有空参构造,防止别人创建private LazyMRong(){}//2.声明自己的唯一实例,先不实例化private static LazyMRong lazyMRong ;//3.对外提供获取此对象的静态方法public synchronized static LazyMRong getLazyMRong() {if (lazyMRong == null) {//懒汉式意味着,只有你在想获取时,才创建此对象return new LazyMRong();}return lazyMRong;}public void teachEnglish() {System.out.println("连夜补习外语!");}
}

3)饿汉式分析:毋庸置疑,它是延迟初始化的,能够在多线程中很好的工作,同时在第一次调用时,才进行初始化,避免了内存的浪费。但是加了synchronized 锁机制,所以效率很低

先解释一下为什么叫饿汉式:“饿”意味着“着急”,饿汉式就是指,不管单例对象有没有外部访问,先实例化再说

【Java代码】饿汉式的M蓉

package com.liyan.hungry;
/*** 饿汉式的M蓉(线程安全)* <p>Title: HungryMRong</p>  * @author  Liyan  * @date    2017年4月27日 下午3:02:18*/
public class HungryMRong {//1.私有空参构造,防止别人创建private HungryMRong(){}//2.创建自己的唯一对象,并直接实例化(饿汉式,因为饿着急,不管三七二十一,直接实例化)private static HungryMRong hungryMRong = new HungryMRong();//3.对外提供获取此对象的静态方法public static HungryMRong getHungryMRong() {return hungryMRong;}
}

4)双重检查锁(DCL,即 double-checked locking)分析:显然饿汉式,不懒,所以没有延迟初始化,同时它基于 classloder 机制,而没用加锁的方式,所以既避免了多线程的同步问题,又使得执行效率得到提高。但是,因为它在类加载时就初始化,所以会造成内存的浪费

先解释一下为什么叫双重检查锁:顾名思义,会涉及到两把锁:

①进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块;

②进入同步块后,再次检查实例是否存在,如果不存在,就在同步的情况下创建该实例。

【Java代码】双重检查锁的M蓉

package com.liyan.dcl;
/*** DCL双重检查锁的单例模式的M蓉* <p>Title: DCLMrong</p>  * @author  Liyan  * @date    2017年4月27日 下午4:54:30*/
public class DCLMrong {//1.私有空参构造,防止别人创建private DCLMrong (){}//2.声明唯一的单例对象;注意这里用到了volatile关键字!private volatile static DCLMrong dclMrong ;//3.对外提供获取此对象的静态方法public static CLMrong getDclMrong() {//第一把锁:先检查实例是否存在,如果不存在才进入下面的同步块;if (dclMrong == null) {synchronized (DCLMrong.class) {//第二把锁:再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例。if (dclMrong == null) {return new DCLMrong();}}}return dclMrong;}
}

分析:这种方式采用双锁机制,安全且在多线程情况下能保持高性能说明: 双重检查加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量

(5)登记式/静态内部类

先解释一下为什么叫登记式:见名知意,想要获取某单例对象,没登记的必须先登记,然后才能获取。此时我们还需要一个登记簿(Map集合),当然,这个登记簿可以记录一堆单例对象,而不仅仅是一个。

这个模式确实复杂了一些,而且很多网上的例子也是觉得有所不妥,我根据个人理解,写了如下的代码,如果各位看官觉得也有疑问,欢迎留言讨论。

【Java代码】

①创建登记式单例模式类

package com.liyan.register;
import java.util.HashMap;
import java.util.Map;
/*** 登记式单例模式* <p>Title: RegisterMrong</p>  * @author  Liyan  * @date    2017年4月27日 下午6:38:22*/
public class RegSingleton {//1.私有空参构造,防止别人创建private RegSingleton(){}//2.创建一个登记簿,在静态代码块中创建自己的唯一对象private static Map<String, RegSingleton> map = new HashMap<String, RegSingleton>(0);  //在登记簿上登记private static void singletonHolder(String name) {if(name==null){  name="RegSingleton"; } RegSingleton singleton = new RegSingleton();  //利用反射获取类名,并作为map集合的keymap.put(name, singleton);System.out.println("已将"+ name +"记录到登记簿!");}//3.对外提供获取此对象的静态方法public static RegSingleton getInstance(String name){  if(name==null){  name="RegSingleton"; System.out.println("名字为空,自动找RegSingleton!");}  //查询登记簿上是否登记过if(map.get(name)==null){  System.out.println("名字"+name+"未在登记簿中登记!");try {  //如果没有登记过,则先进行登记System.out.println("名字"+name+"开始在登记簿中登记!");RegSingleton.singletonHolder(name);//登记之后再返回该对象System.out.println("名字"+name+"已经登记完成!");return map.get(name);} catch (Exception e) {  e.printStackTrace();  }  }else {//如果登记过,直接返回该单实例System.out.println("名字"+name+"之前在登记簿中登记过了!");return map.get(name);  }return null;} public void teachEnglish() {System.out.println("连夜补习外语!");}
}

②创建外部访问

package com.liyan.register;
/*** S喆请M蓉补习外语* <p>Title: SZhe</p>  * @author  Liyan  * @date    2017年4月27日 下午2:15:23*/
public class SZhe {public static void main(String[] args) {RegSingleton mrong = RegSingleton.getInstance("Mrong");mrong.teachEnglish();}
}

③结果

名字Mrong未在登记簿中登记!
名字Mrong开始在登记簿中登记!
已将Mrong记录到登记簿!
名字Mrong已经登记完成!
连夜补习外语!

①相同点:都利用ClassLoder机制,来保证初始化时只有一个线程。分析:主要是比较一下,登记模式和双重检验锁式有何异同?

②不同点:双重检验锁式在类一被装载是就被初始化了,所以它没有延迟的效果;而登记模式,只有再主动调用获取该对象的静态方法时,才被初始化,所以它有延迟效果。

(6)枚举式

先解释一下为什么叫枚举式:不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,但是《Effective Java》一书中的话有这样一段很经典的话:“单元素的枚举类型已经成为实现Singleton的最佳方法!

【Java代码】枚举单例模式的M蓉

package com.liyan.enummodel;
/*** 枚举单例模式的M蓉* <p>Title: EnumMrong</p>  * @author  Liyan  * @date    2017年4月27日 下午9:23:59*/
public class EnumMrong {//1.私有空参构造,防止别人创建private EnumMrong() {}//2.申明自己的唯一对象public static EnumMrong getInstance() {return Singleton.INSTANCE.getInstance();}//3.对外提供获取此对象的静态方法private static enum Singleton {INSTANCE;private EnumMrong singleton;//在构造方法中实例化对象,保证只调用一次private Singleton() {singleton = new EnumMrong();}public EnumMrong getInstance() {return singleton;}}
}

 

【秒懂设计模式】单例设计模式相关推荐

  1. 面试官最喜欢考的设计模式---单例设计模式

    一.讲在前面的废话 笔者曾经多次面试都遇到了这个问题,请你说一下软件的设计模式,一般我们都会把23种模式例举几个出来,如果没有说错,你一定会列举单例设计模式,这样的话你就中了面试官的圈套了.接下来的一 ...

  2. Java设计模式——单例设计模式/权限修饰符的使用

    1. 单例模式含义 所谓的单例设计模式,就是采取一定的方法保证整个软件系统中,某个类只能存在一个对象实例. 单例设计模式 2. 单例设计模式的两种实现方法 饿汉式:不管是否需要该实例,我事先就把该实例 ...

  3. Java设计模式—单例设计模式(Singleton Pattern)完全解析

    转载请注明出处:http://blog.csdn.net/dmk877/article/details/50311791 相信大家都知道设计模式,听的最多的也应该是单例设计模式,这种模式也是在开发中用 ...

  4. Java设计模式-单例设计模式

    文章目录 前言 一.什么是设计模式? 二.单例模式介绍 1.单例模式的目的 2.单例模式的定义 单例模式的设计与实现要点: 3.单例模式的两种实现方式 (一)饿汉式创建 (二)懒汉式创建 4.单例模式 ...

  5. java设计模式--单例设计模式

    package com.henu; /*饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了.而懒汉比较懒,只有当调用getInstance的时候,才回去初始化 ...

  6. python基础知识整理 第七节:单例设计模式、异常、模块、包、制作模块、文件

    1.单例设计模式 单例设计模式就是为对象在内存中分配空间的时候,永远只会返回一个唯一的固定的内存空间.这样就能保证在内存中这个类的对象只有唯一的一份,这个就叫做单例.(为对象分配空间使用的是内置方法_ ...

  7. 第三次学JAVA再学不好就吃翔(part117)--单例设计模式

    学习笔记,仅供参考,有错必纠 文章目录 多线程 单例设计模式 饿汉式 懒汉式 第三种格式 Timer类 线程的五种状态 多线程 单例设计模式 单例设计模式,即保证类在内存中只有一个对象,如何保证类在内 ...

  8. C++11标准下的单例设计模式

    单例设计模式 设计模式(Design Pattern)是一套被反复使用.多数人知晓的.经过分类的.代码设计经验的总结. 为什么会产生设计模式这样的东西呢? 使用设计模式的目的:为了代码可重用性.让代码 ...

  9. 【Java】day9--main方法、单例设计模式、继承、方法重写部分知识点总结

    (一)main方法 jvm调用main方法,jvm也是一个程序     main方法详解:         public:公共  保证该类在任何情况下,jvm都对其方法可见.         stat ...

最新文章

  1. 码农技术炒股之路——配置管理器、日志管理器
  2. 【OpenCV 4开发详解】QR二维码检测
  3. WPF 窗口居中 变更触发机制
  4. Android FrameWork学习(二)Android系统源码调试
  5. LB Cluster 之一:集群及LVS基础理论详解
  6. 微软.NET各技术应用前景 针对vs.net2010
  7. Elasticsearch 基本介绍及其与 Python 的对接实现
  8. 从 json 文件到炫酷动画 - Lottie 实现思路和源码分析
  9. nyoj--77--开灯问题
  10. oracle数据库速度测试,ORACLE数据库测试数据插入速度
  11. 艾伟:WinForm控件开发总结(三)------认识WinForm控件常用的Attribute
  12. 三星6818核心板接口众多兼容三星4418开发板
  13. 迪乐尼童鞋防骗子—常见网络骗子骗术防御要点
  14. Java EE之FreeMarker前度模板引擎的使用
  15. 常用10个Excel快捷键,提高工作效率
  16. python哪里最难用_Python里最难的Asyncio,这里有一份非常适合小白的教程
  17. 如何把html文件格式转为视频格式,如何将qlv格式转换成mp4-太平洋软件资讯-太平洋电脑网...
  18. SMOTE-类不平衡问题
  19. chrome浏览器f12的network里的请求行变得很大的解决方案
  20. c语言魔王语言上机报告,魔王语言报告(带有完整程序).doc

热门文章

  1. 软件生命周期管理研讨会有感
  2. 减治法解决约瑟夫斯问题(JAVA)
  3. inventor如何钣金出弧面_Inventor教程之钣金多规则
  4. arduino使用oled代码_【惊不?】Arduino改造古董卡西欧计算器为作弊神器
  5. Spring整合Mybatis和JUnit
  6. asp连接oracle6,asp下用OracleInProcServer完成对Oracle的连接和操作-ASP教程,数据库相关...
  7. 虚拟机安装rsync服务器配置,虚拟机安装rsync服务器配置
  8. 吗 支持windows_M1能否原生支持Windows 苹果把问题抛给了微软
  9. nginx动静分离配置_Nginx动静分离
  10. vue中自定义指令、组件化、生命周期、节流和防抖、获取DOM、mint-ui简介、过渡和动画