设计模式之单件模式(Singleton Pattern)
一.单件模式是什么?
单件模式也被称为单例模式,它的作用说白了就是为了确保“该类的实例只有一个”
单件模式经常被用来管理资源敏感的对象,比如:数据库连接对象、注册表对象、线程池对象等等,这种对象如果同时存在多个的话就会造成各种不一致的麻烦(你总不希望发生数据库重复连接的异常吧)
二.如何保证类的实例只有一个?
(这个问题看似简单,但如果没有接触过单件模式的话,要自己想出来解决方案还是需要一些天赋的。。不信的话,可以试着想想。。)
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来加载单件类,这样就有效避免了上述问题
六.总结
应用单件模式可以保证对象的唯一性,但要注意单件模式的适用范围
不应该滥用单件模式,因为毕竟需要管理的资源敏感对象不会很多
设计模式之单件模式(Singleton Pattern)的更多相关文章
- 设计模式 - 单件模式(singleton pattern) 具体解释
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u012515223/article/details/28595349 单件模式(singleton ...
- 设计模式----创建型型模式之单件模式(Singleton pattern)
单件模式,又称单例模式,确保一个类只有一个实例,并提供全局访问点. 单件模式是比较简单且容易理解的一种设计模式.只有一个实例,通常的做法...TODO 类图比较简单,如下所示: 示例代码: 懒汉模式( ...
- C#设计模式——单件模式(Singleton Pattern)
一.概述在软件开发过程中,我们有时候需要保证一个类仅有一个实例,比如在一个电脑用户下只能运行一个outlook实例.这时就需要用到单件模式.二.单件模式单件模式保证一个类仅有一个实例,并提供一个访问它 ...
- 1.单件模式(Singleton Pattern)
意图:为了保证一个类仅有一个实例,并提供一个访问它的全局访问点. 1.简单实现(多线程有可能产生多个实例) public class CommonSigleton { /// <summary& ...
- 单件模式Singleton来控制窗体被重复或多次打开
本文转载:http://blog.csdn.net/a0700746/article/details/4473796 一般在百度搜一下,会出来一下内容,看来很好用.Singleton很方便的一个用处就 ...
- 乐在其中设计模式(C#) - 提供者模式(Provider Pattern)
原文:乐在其中设计模式(C#) - 提供者模式(Provider Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 提供者模式(Provider Pattern) 作者:weba ...
- 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern)
原文:乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) 作者:webabc ...
- 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)
原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...
- 乐在其中设计模式(C#) - 状态模式(State Pattern)
原文:乐在其中设计模式(C#) - 状态模式(State Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 状态模式(State Pattern) 作者:webabcd 介绍 允 ...
随机推荐
- 关于easyui-datagrid数据表格, 分页取出数据
在制作数据表格的时候有一个这样的属性, pagination是否显示分页列表, 分页显示的时候需要分别从数据库中取数据, 每页显示几行, 即只从数据库取出几行数据来显示, 具体代码如下 1, 显示页面 ...
- mysql增加远程连接用户及查看数据库表结构
一.增加远程连接用户 1.用root权限登录数据库 2.加用户:grant all privileges on *.* to '111'@'192.168.1.%' identified by '2 ...
- Writing A Better JavaScript Library For The DOM 阅读记录
原文地址:http://coding.smashingmagazine.com/2014/01/13/better-javascript-library-for-the-dom/ 主要观点: live ...
- jquery源码学习-初始(1)
最近几天一直在研究jquery源码,由于水平太低看得昏头转向.本来理解的也不是很深刻,下面就用自己的想法来说下jquery是如何定义构造函数初始化的.如果有什么不对的地方,希望个位高手指出. 首先要了 ...
- StartServiceCtrlDispatcher
服务程序通常编写成控制台类型的应用程序,总的来说,一个遵守服务控制管理程序接口要求的程序 包含下面三个函数: 1.服务程序主函数(main):调用系统函数 StartServiceCtrlDispat ...
- 启动多个eclipse 时,因为一个另一个启动报错,
启动多个eclipse 时,因为一个另一个启动报错, 原因: 可能是 有一个 eclipse 中 的 tomcat 配置出错:preference中 tomcat 配置 context dec ...
- Javascript 常用扩展方法
这篇文章纯粹是为了保存这些方法,供以后翻阅,其实一直保存在 evernote 里面,但觉得还是放到对的地方会好点. 现在收录的很少,希望以后会慢慢增多. 数组扩展 contains,remove 扩展 ...
- 2018.06.29 NOIP模拟 1807(简单递推)
1807 题目背景 SOURCE:NOIP2015-SHY-2 题目描述 给出一个由数字('0'-'9')构成的字符串.我们说一个子序列是好的,如果他的每一位都是 1.8.0.7 ,并且这四个数字按照 ...
- 28. Bad Influence of Western Diet 西式饮食的消极影响
28. Bad Influence of Western Diet 西式饮食的消极影响 ① The spread of Western eating habits around the world i ...
- gj3 Python数据模型(魔法函数)
3.1 什么是魔法函数 类里面,实现某些特性的内置函数,类似 def __xx__(): 的形式. 不要自己定义XX,并不是和某个类挂钩的 class Company(object): def __i ...