一.单件模式是什么?

单件模式也被称为单例模式,它的作用说白了就是为了确保“该类的实例只有一个”

单件模式经常被用来管理资源敏感的对象,比如:数据库连接对象、注册表对象、线程池对象等等,这种对象如果同时存在多个的话就会造成各种不一致的麻烦(你总不希望发生数据库重复连接的异常吧)

二.如何保证类的实例只有一个?

(这个问题看似简单,但如果没有接触过单件模式的话,要自己想出来解决方案还是需要一些天赋的。。不信的话,可以试着想想。。)

1.类的实例可能只有一个吗?貌似只要知道类名就可以随便new了吧?

当然可以,知道类名的话,确实可以调用其构造方法来new实例,但是,注意一点:这个类必须要有公开的构造方法才能从外部new实例,不是吗?

2.那就是说要保证类的实例只有一个的话,这个类不能有公开的构造方法,对吧?

没错,就是这样,我们需要定义一个私有的构造方法

3.一个没有公开构造方法的类能够产生实例吗?如果构造方法是private,那么只有该类的实例才能调用这个构造方法,同样的要调用这个构造方法才能产生该类的实例。。这不是“鸡生蛋,蛋生鸡。。”的问题吗?

用私有的构造方法当然可以生产实例,上面忽略了一点:并不是“只有该类的实例才能调用这个构造方法”

因为在该类内部就可以随便调用这个私有的构造方法,并不需要创建任何实例

-------

有了上面的讨论结果,我们就可以实现经典的单件模式了:

package SingletonPattern;/*** @author ayqy* 最经典的单件模式*/
public class Singleton {private static Singleton instance;//定义静态实例变量/*** 定义私有构造方法,防止从外部new实例*/private Singleton(){//初始化操作}/*** 提供全局访问点* @return 该类的实例*/public static Singleton getInstance(){if(instance == null)instance = new Singleton();return instance;}/** 其它有用的属性和行为* 毕竟应用了单件模式的类仍然具有原本的功能* */
}

注意:一定要清楚最后一点,应用了单件模式的类并不应该丧失其原本的功能,千万不能为了使用而使用

三.继续思考我们的单件模式

我们的单件模式已经万无一失了吗?

不,它还存在很多问题,比如:

1.多线程环境下

2.多个class loader环境下

我们无法保证产生的实例只有一个,对吧?

但是作为一种成熟的设计模式,单件模式必须要能从容应对这些环境,所以,接下来我们将讨论如何应对这些环境

四.多线程环境下的单件模式

如何在多线程环境下保证实例的唯一性?

很容易想到用synchronized关键字来保证线程安全,就像这样:

/*** 提供全局访问点* @return 该类的实例*/
public static synchronized Singleton getInstance(){if(instance == null)instance = new Singleton();return instance;
}

我们把getInstance方法定义为同步方法就保证了不会有多个线程同时进入该方法,就不会产生不同的实例了

-------

但是上面的方法存在致命的问题:用synchronized关键字同步方法会极大的降低效率(同步一个方法甚至可能造成百倍的效率下降。。),这会拖垮我们的程序

有什么好的改进方法呢?

首先,上面的同步块是整个getIntance方法,每次调用该方法都会强制进入同步机制,但仔细一想,我们只在第一此调用该方法时需要进行同步(第一次new对象),之后的调用直接返回new好的对象就好了

那么,我们的改进方案就是:用双重加锁实现只在第一次new对象时进行同步

package SingletonPattern;/*** @author ayqy* 多线程下的单件模式2——利用双重加锁保证只在实例化变量的时候进行同步*/
public class DoubleLockSingleton {private static volatile DoubleLockSingleton instance;//定义静态实例变量/*** 定义私有构造方法,防止从外部new实例*/private DoubleLockSingleton(){//初始化操作}/*** 提供全局访问点* @return 该类的实例*/public static DoubleLockSingleton getInstance(){if(instance == null)synchronized(DoubleLockSingleton.class){//进入同步块if(instance == null)//再次判空instance = new DoubleLockSingleton();}return instance;}/** 其它有用的属性和行为* 毕竟应用了单件模式的类仍然具有原本的功能* */
}

注意:双重加锁体现在volatile关键字(告诉编译器,这个变量不能被保留副本,一旦发生变动就会强制写回,避免了不一致)和synchronized修饰的同步块

但要明白这样做的代价,volatile关键字也会告诉编译器,不要对该对象进行编译优化

只看第一次new对象的过程的话,双重加锁的效率甚至要比同步方法更低,但在双重加锁方式在以后的调用中不再需要进行同步,所以长远看来双重加锁的效率要高于同步方法

-------

有没有一种方法不需要使用龟速的同步机制就能保证线程安全呢?如果有的话,绝对能够大大提高效率,对吧?

当然有,这种方法叫做“急切初始化”(顺便提一下,开篇提到的“经典单件模式”其实用了“延迟初始化”的方法。。很简单,不必解释),一起看看吧:

package SingletonPattern;/*** @author ayqy* 多线程环境下的单件模式——用“急切初始化”来保证线程安全*/
public class EagerlyInitSingleton {private static EagerlyInitSingleton instance = new EagerlyInitSingleton();//定义静态实例变量,并在类加载的时候就进行初始化操作/*** 定义私有构造方法,防止从外部new实例*/private EagerlyInitSingleton(){//初始化操作}/*** 提供全局访问点* @return 该类的实例*/public static synchronized EagerlyInitSingleton getInstance(){return instance;}/** 其它有用的属性和行为* 毕竟应用了单件模式的类仍然具有原本的功能* */
}

额,这也能叫方法吗?这么做貌似不和标准吧?

没关系,这种方法自然有它的优点,比如:

1.效率很高,且线程安全

2.简单易用,什么都不用考虑,甚至不用判断

但其致命的缺点是:资源浪费问题,如果这个对象是一个巨大的极其耗费资源的对象,而我们在一开始就创建了它,却迟迟没有用到,这将是非常伤的。。

-------

上面提到了三种保证线程同步的方式,如何选择必须要结合具体情况来定,应综合考虑效率,资源利用等各个因素

五.多个class loader环境下的单件模式

如果存在多个类加载器,多个类加载器可能同时加载我们的单件类,从而产生多个实例

对于这种情况,我们可以显式指定使用哪一个class loader来加载单件类,这样就有效避免了上述问题

六.总结

应用单件模式可以保证对象的唯一性,但要注意单件模式的适用范围

不应该滥用单件模式,因为毕竟需要管理的资源敏感对象不会很多

转载于:https://www.cnblogs.com/ayqy/p/3962910.html

设计模式之单件模式(Singleton Pattern)相关推荐

  1. 11单件模式(Singleton Pattern)

    创建型模式---单件模式(Singleton Pattern) 动机(Motivation):     在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确 ...

  2. 单件模式Singleton Pattern

    单件模式:确保一个类只有一个实例,并提供一个全局访问点. 一般思路: 问题: 当为多线程时,可能会创建两个或多个实例,如: 解决方案一:同步,但会降低性能 方法二:使用"急切"创建 ...

  3. 单件模式 Single Pattern HeadFirst学习系列

    单件模式 Single Pattern 独一无二的对象 定义:单件模式确保程序中一个类最多只有一个实例,并提供全局访问.  实现单件模式需要私有的构造器.一个静态方法和一个静态变量.  确定在性能和资 ...

  4. 乐在其中设计模式(C#) - 提供者模式(Provider Pattern)

    原文:乐在其中设计模式(C#) - 提供者模式(Provider Pattern) [索引页] [源码下载] 乐在其中设计模式(C#) - 提供者模式(Provider Pattern) 作者:web ...

  5. 乐在其中设计模式(C#) - 原型模式(Prototype Pattern)

    [索引页] [源码下载] 乐在其中设计模式(C#) - 原型模式(Prototype Pattern) 作者:webabcd 介绍 用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象. ...

  6. 乐在其中设计模式(C#) - 命令模式(Command Pattern)

    原文:乐在其中设计模式(C#) - 命令模式(Command Pattern) [索引页] [源码下载] 乐在其中设计模式(C#) - 命令模式(Command Pattern) 作者:webabcd ...

  7. java 访客模式,设计模式 - 访客模式( Visitor Pattern)

    设计模式 - 访客模式( Visitor Pattern) 在Visitor模式中,我们使用一个访问者类来更改元素类的执行算法. 通过这种方式,元素的执行算法可以随着访问者的变化而变化. 此模式属于行 ...

  8. 如何让孩子爱上设计模式 ——11.外观模式(Facade Pattern)

    如何让孩子爱上设计模式 --11.外观模式(Facade Pattern) 标签: 设计模式初涉 场景引入 相信各位玩过LOL英雄联盟游戏的童鞋,对下面两个英雄都不会陌生吧:       分别是瑞雯和 ...

  9. 如何让孩子爱上设计模式 ——10.桥接模式(Bridge Pattern)

    如何让孩子爱上设计模式 --10.桥接模式(Bridge Pattern) 标签: 设计模式初涉 我有故事,你有酒吗?这年头写个技术文不讲个故事都不行,行,我讲: 还有发现很多的技术博文都开始有喜欢往 ...

最新文章

  1. 线性代数与矩阵论 定理 1.5.6 拉格朗日插值公式
  2. 学习旧岛小程序 (2) 自定义组件
  3. session outline for different culture
  4. lua实现多继承-方式1
  5. Segment,Path,Ring和Polyline对象
  6. 【牛客NOIP模拟】牛半仙的魔塔(增强版)【贪心】【并查集】
  7. 发送广播BroadcastReceiver
  8. 卖身字节跳动的互动百科或被改名
  9. 33 个 JavaScript 核心概念系列(四): == 与 ===
  10. java线程跟多线程
  11. RK平台LCD调试说明
  12. 软件项目管理课程总结
  13. 2022全新车型汽车配置参数数据库大全
  14. 方正飞鸿中间件大赛落幕 大学生项目获收购
  15. python实现税后工资_python税后工资计算器
  16. ptp精准时间协议_精确时间协议PTP研究
  17. 【UIAutomator2】实现微信自动加好友功能
  18. 关于三极管,我忽视了两点
  19. android目录结构
  20. 【良心】C语言零基础学习,C语言初学者入门基础知识讲解

热门文章

  1. android 在什么情况下会主动gc_Python 什么情况下会生成 pyc 文件?
  2. 合工大850参考书_合肥工业大学计算机考研850上岸经验分享
  3. java fx choicebox_JavaFX:具有图像和文本的ChoiceBox
  4. 怎样删去csv中重复行_4个锦囊,祝你快速删去Excel中的重复数据
  5. go环境搭建_学习的golang第一步,搭建我们运行的环境,go! go! go
  6. tensorflow 小于_坐姿不对,屏幕就变模糊!教你用TensorFlow做一款“隐形背背佳”...
  7. 【渝粤题库】国家开放大学2021春1708农业产业发展规划题目
  8. zigbee协议技术无线模块对智能家居市场前景分析
  9. 成都亿佰特物联网无线数传专家:lora无线传输模块网关技术的优缺点
  10. 百分制转化为五级制java_javav 的日志-编写存储过程,将百分制成绩,转换成绩等级’A’,’B’,’C’,’D’,’E’...