什么是单例模式

单例模式是23种设计模式中最简单和易用的模式。在某些情境下,如在一个上市公司中,有很多不同级别的员工,但是公司的CEO或者CTO都是只有一个的,CEO或者CTO在公司里就要求是一个单例。单例模式,就是某个类因实际情况的需要,要求在全局的范围内只能有唯一的实例对象,这个对象是常驻内存的,可以重复使用,降低重复创建对象的开销。

单例模式的特点

  • 类的构造函数是私有的
  • 在类内部实例化对象,并通过静态方法向外提供实例化的对象

下面主要讲解实现单例模式的方法以及它们的优缺点

单例模式的实现

单例模式的目的,就是要确保在全局范围内某个类的对象是唯一的。所以实现单例模式时,我们至少要考虑两个影响对象创建的因素。

  • 在并发的环境下的线程安全
  • 反序列化

饿汉实现

在类第一次加载时,就进行对象的实例化。

public class SingletonDemo {private final static SingletonDemo mSingletonDemo = new SingletonDemo();private SingletonDemo() {}public static SingletonDemo getInstance() {return mSingletonDemo;}}
复制代码

懒汉实现

在类加载时不进行对象的实例化,只在对象被第一次访问时,才进行对象的实例化。

public class SingletonDemo {private static SingletonDemo mSingletonDemo;private SingletonDemo() {}public static SingletonDemo getInstance() {if(mSingletonDemo == null) {mSingletonDemo = new SingletonDemo();}return mSingletonDemo;}}
复制代码

明显,在多线程的环境下,上面两种实现方式都不是线程安全的。为了实现线程安全,我们首先可以想到使用synchronized关键字。

线程安全的懒汉模式

public class SingletonDemo {private static SingletonDemo mSingletonDemo;private SingletonDemo() {}public static synchronized SingletonDemo getInstance() {if(mSingletonDemo == null) {mSingletonDemo = new SingletonDemo();}return mSingletonDemo;}}
复制代码

关于synchronized关键字说明一下,synchronized声明的静态方法,同时只能被一个执行线程访问,但是其他线程可以访问这个对象的非静态方法。即:两个线程可以同时访问一个对象的两个不同的synchronized方法,其中一个是静态方法,一个是非静态方法。

所以,当有多个线程同时访问getInstance静态方法时,多个其他的线程只能等待,这时只有一个线程能够访问getInstance方法,等这个线程释放后其他线程才能访问。这样就会影响速度和效率。

为了提高懒汉模式的速度和效率,可以减小锁的粒度和次数。

双重校验锁法

public class SingletonDemo {private static SingletonDemo mSingletonDemo;private SingletonDemo() {}public static synchronized SingletonDemo getInstance() {if(mSingletonDemo == null) {synchronized (SingletonDemo.class) {if(mSingletonDemo == null) {mSingletonDemo = new SingletonDemo();}}}return mSingletonDemo;}}
复制代码

从上面可以看到,只有在第一次访问时才会锁定和创建类的对象,之后的访问都是直接使用已经创建好的对象,这样减少锁定的次数和范围,以达到提高单例模式的效率。

但是,对象的实例化,并不是一个原子性操作。即第11行代码处,它可以分成下面三个步骤: 1、new SingletonDemo(),为SingletonDemo实例分配内存 2、调用SingletonDemo的构造器,完成初始化工作 3、将mSingletonDemo指向分配的内存空间

由于java处理器可以乱序执行,即无法保证2和3的执行顺序。这对双重校验锁法实现的单例模式有什么影响呢? 当第一个线程访问getInstance方法时,会锁定临界区(第9行到第13行代码),它实例化对象的顺序是1=>3=>2,而在这时如果有第二个线程来访问getInstance方法,由于第一个线程在处理器中执行完了3未执行2,第二个线程会马上得到实例对象,因为第一个线程的3已经执行完即mSingletonDemo已经不为空。当第二个线程使用没有初始化的对象时就会出现问题。

所以,双重校验锁法也不是完美的,在并发环境下依然可能出现问题。

静态内部类实现

public class SingletonDemo {private static SingletonDemo mSingletonDemo;private SingletonDemo() {}private static class SingletonHolder {private static final SingletonDemo INSTANCE = new SingletonDemo();}public static SingletonDemo getInstance() {return SingletonHolder.INSTANCE;}}
复制代码

第一次加载SingletonDemo类时并不会实例化INSTANCE,只有在第一次调用getInstance方法时,才会加载SingletonHolder内部类,创建SingletonDemo实例。这种方式不仅确保了线程安全,也保证单例对象的唯一性,同时也实现了单例对象的懒加载。

枚举实现

上面几种实现方式,可能会因为反序列化而创建新的实例,所以必须重写readResolve方法,在readResolve方法中返回已经创建的单例。

使用枚举可以很简单的实现单例模式,这也是Effective Java中提倡的方式。因为枚举本身就是类型安全的,并且枚举实例在任何情况下都是单例。

public enum SingletonEnumDemo {INSTANCE;public void justDoYourThing() {}
}
复制代码

枚举单例使用

SingletonEnumDemo.INSTANCE.justDoYourThing();
复制代码

容器实现

public class SingletonDemo {private static Map<String, Object> singletonMap = new HashMap<String, Object>();private SingletonDemo() {}public static void registerService(String key, Object instance)     {if (!singletonMap.containsKey(key)) {singletonMap.put(key, instance);}}public static Object getService(String key) {return singletonMap.get(key);}}
复制代码

转载于:https://juejin.im/post/5ac4458151882555867fa2ed

23种设计模式之《单例模式》相关推荐

  1. java 23种设计模式 04 单例模式

    java 23种设计模式 04 单例模式 一.什么是单例模式 单例模式是一种对象创建型模式,使用单例模式,可以保证为一个类只生成唯一的实例对象.也就是说,在整个程序空间中,该类只存在一个实例对象.   ...

  2. 23种设计模式之单例模式、工厂模式、原型模式、建造者模式

    系列文章目录 第一章:程序设计原则-单一职责.接口隔离.依赖倒置.里式替换 第二章:程序设计原则-开闭原则.迪米特法则.合成复用原则 文章目录 系列文章目录 一.设计模式简单介绍 1.1.什么是设计模 ...

  3. Java面试23种设计模式之单例模式的8种实现方式

    单例模式8中实现方式 1.单例模式介绍 2.单例模式的八种方式 3.饿汉式(静态常量),这种单例模式可用,可能造成内存浪费. 4.饿汉式(静态代码块),这种单例模式可用,可能造成内存浪费. 5.懒汉式 ...

  4. 《23种设计模式之单例模式(4种实现)》

    说在前头:本人为大二在读学生,书写文章的目的是为了对自己掌握的知识和技术进行一定的记录,同时乐于与大家一起分享,因本人资历尚浅,能力有限,文章难免存在一些错漏之处,还请阅读此文章的大牛们见谅与斧正.若 ...

  5. 23种设计模式之单例模式

    单例模式 单例模式(Singleton Pattern)是一个比较简单的模式,其定义为:Ensure a class has only one instance,and provide a globa ...

  6. JAVA设计模式总结之23种设计模式

    一.什么是设计模式                                                                                           ...

  7. 23种设计模式之模板方法

    23种设计模式总篇:chenmingyu.top/design/ 模板方法 模板方法属于行为型模式 定义:定义一个操作中的算法的框架,而将一些步骤延迟到子类中.使得子类可以不改变一个算法的结构即可重定 ...

  8. JAVA设计模式总结之23种设计模式(重点!!!)

    JAVA设计模式总结之23种设计模式: 一.什么是设计模式 设计模式遵循的原则有6个: 二.设计模式的三个分类 三.各分类中模式的关键点 四.概说23种设计模式 1.单例模式(Singleton) 2 ...

  9. java 的23种设计模式 单例模式

    23种设计模式友情链接: 点击打开链接 单例模式: A.饿汉式单例模式 具体步骤: 1.声明一个私有的静态的最终的本类类型的对象并实例化 private static final Person ins ...

最新文章

  1. 常见的集合容器应当避免的坑
  2. pika-NoSQL原理概述
  3. 写markdown用于Github上readme.md文件
  4. 合肥python培训-合肥Python测试开发全栈核心课程
  5. tornado环境搭建
  6. 文本转换成htmldocument
  7. Python继承类的方式实现多线程及控制线程数
  8. Burpsuite爆破含CSRF-Token的程序
  9. [线程池] ------ 形象的描述线程池,用一个特好记的例子来记忆
  10. Web Broadcast Channel
  11. springboot 项目中在普通类中调用dao层的mapper 出现空指针异常
  12. 大数据之Superset
  13. 运营商5G商业模式研究
  14. Python去除文件名后缀
  15. u盘打不开,提示需要格式化怎么办?
  16. C4D玻璃材质调节方法
  17. MATLAB 数学应用 微分方程 一维偏微分方程 求解单个PDE
  18. windows编程学习感悟
  19. 使用OpenCV合成训练图片,同时生成labelme兼容格式的标注文件
  20. pe下找不到ssd硬盘_进入pe后找不到固态硬盘怎么解决

热门文章

  1. maven nexus 私服的搭建学习
  2. 82.开始→运行→输入的命令集锦
  3. window.showModalDialog介绍
  4. 【从零入门 Web 前端】HTML5 + CSS 简明教程
  5. MySQL高级 - 案例 - 系统性能优化分析
  6. 阻塞队列的使用案例-注册成功后增加积分
  7. Spring 提供哪些配置形式?
  8. 权限管理-SpringSecurity介绍
  9. 通过反射获取带参构造方法并使用
  10. 文件下载的文件名中文乱码