设计模式之单件模式(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来加载单件类,这样就有效避免了上述问题

六.总结

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/359589.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

asp.net中两款文本编辑器NicEdit和Kindeditor

分类: C#/ASP.Net 2012-10-09 22:35 665人阅读 评论(0) 收藏 举报 文本编辑asp.nettextboxserveraspsafari目录(?)[] 做过Web开发的朋友相信都使用过富文本编辑器,比较出名的CuteEditor和CKEditor很多人应该已经使用过,在功能强大的同时需要…

导出oracle awr分析报告,配置oracle内存参数,察看表空间使用率

cmd 命令生成awr报告: cmd 窗口 输入 -> Sqlplus sys/orclorcl as sysdba (sys登陆oracle).导出awr命令 ?/rdbms/admin/awrrpt.sql 3..输入导出的文件格式 为 html 回车 4输入数字1 为导出今天的分析报告 ,2 3 。。。,回车 5.输入开始 snap id…

android多点触控自由对图片缩放

在系统的相册中,观看相片就可以用多个手指进行缩放.要实现这个功能,只需要这几步:1.新建项目,在项目中新建一个ZoomImage.javapublic class ZoomImageView extends View {//初始化状态常量public static final int STATUS_INIT1;//图片放大状态常量public static final int STA…

台阶问题额

题目名字 台阶问题 题意 初始化数组f的第一个元素为1。这表示到达第0级台阶的方式数为1,即不需要迈任何台阶。从第1级台阶开始,迭代计算每一级台阶的不同方式数。对于当前台阶i,内部循环从1到i和k中的较小值开始迭代。这是因为每次只能向上…

jboss eclipse_调试生产服务器– Eclipse和JBoss展示

jboss eclipse您是否编写有错误的代码? 不,当然不是。 对于我们其余的人,他们确实会编写带有bug的代码,我想解决一个非常敏感的问题:调试在生产服务器上运行的应用程序。 因此,您的应用程序已准备好进行部…

Chrome 控制台不完全指南

Chrome的开发者工具已经强大到没朋友的地步了,特别是其功能丰富界面友好的console,使用得当可以有如下功效: 更高「逼格」更快「开发调试」更强「进阶级的Frontender」Bug无处遁形「Console大法好」console.log 大家都会用log,但鲜…

datanucleus_DataNucleus 3.0与Hibernate 3.5

datanucleus如官方产品站点所述, DataNucleus Access Platform是现有的最符合标准的开源Java持久性产品。 它完全符合JDO1 , JDO2 , JDO2.1 , JDO2.2 , JDO3 , JPA1和JPA2 Java标准。 它还符合OGC简单功能规…

$.fn.zTree 展开 回显 选中

var ids节点字符串;// 字符串形式:选中节点,...,二级节点,根节点 var siids..split(",").length-1; var setting {view:{selectedMulti:false},data:{simpleData:{enable:true}}, async: { enab…

Android学习笔记——Menu(二)

知识点:这次将继续上一篇文章没有讲完的Menu的学习,上下文菜单(Context menu)和弹出菜单(Popup menu)。 上下文菜单上下文菜单提供对UI界面上的特定项或上下文框架的操作,就如同Windows中右键菜单一样。 在Android中,有两种提供上…

eclipse卡慢解决办法

1.设置JVM运行内存 1.1编辑eclipse.ini 1.2 编辑eclipse.ini,设置jvm运行内存参数,最小内存:物理内存*0.2, 最大内存: 物理内存*0.6; 其中-vmargs为必须添加参数(-vmargs的意思是设置JVM参数),…

python学习之文件读写

实现文件的读写 #! /usr/bin/python file_add open(test.txt,a)for i in range(1,5): file_add.write("1.1.1.%d 255.255.255.255 %d 2.2.2.%d 255.255.255.168 \n" %(i,i,i))file_add.close() 期间遇到的问题: 1、字符串格式化问题—多个参数 -sh-4.1#…

ext3 tree tbar 初始化定义

var rc_store new Ext.data.SimpleStore( { fields : [ value, text ], data : [ [ 10, 行 ], [ 20, 列 ] ] }); var bar_v [ { text:展开, handler:function(){ tree.expa…

xuggler 中文开发_Xuggler开发教程

xuggler 中文开发大家好, 在这篇文章中,我想介绍JavaCodeGeeks上的一些很酷的新教程。 他们将讨论与Xuggler , FFmpeg和Wowza进行媒体(音频/视频)操纵的方式。 我将在这篇文章中跟踪所有相关的教程。 您可以通过查看P…

QQ游戏百万人同时在线服务器架构实现

转载自:http://morton5555.blog.163.com/blog/static/976407162012013112545710/# QQ游戏于前几日终于突破了百万人同时在线的关口,向着更为远大的目标迈进,这让其它众多传统的棋牌休闲游戏平台黯然失色,相比之下,联众…

Ext grid js上移下移样例

function moveup(){//上移 var grid odin.ext.getCmp(gridid); var sms grid.getSelectionModel().getSelections(); var store grid.store; if (sms.length < 0) { alert(请选中需要上移的行!) return; } var selectdata sms[0]; …

Delphi IDE使用的一些主要技巧

Delphi IDE使用的一些主要技巧 1、查找和替换 &#xff08;1&#xff09;<ctrl>F[1]&#xff1a;选择页“Find”&#xff0c;进行查找&#xff0c;则根据查找方向继续查找。选择页“Find in Files”&#xff0c;则进行该工程内的全文查找。&#xff08;2&#xff09;<…

swing 选择对话框_Java Swing –日期选择器对话框

swing 选择对话框房子里有Swing开发人员吗&#xff1f; 对于使用Swing的用户来说&#xff0c;这是一个GUI组件&#xff0c;可能会对您的UI编码工作有所帮助。 我们的JCG合作伙伴之一提供了日期选择器小部件。 看看这个&#xff1a; Java Swing –日期选择器对话框以选择日期 …

域模型向左走(充血),向右走(贫血)

在文章的开始,我先举一个例子  美国M4谢尔曼坦克 VS德国的虎式坦克(相关资料如下http://mil.eastday.com/m/20070515/u1a2833237.html) 5&#xff1a;1 在五一期间&#xff0c;电视节目中的二战武器大对决吸引了我&#xff0c;其中当美国大兵说他们在用5辆坦克的代价来换德国…

hibernate session 新增、删除,修改、查询样例

1.实体查询 String sqla "from TEST where id? order by sortnum desc"; List list sess.createQuery(sql).setString(0, id).list(); Test test (Test)list.get(0); List list sess.createSQLQuery("select * from TEST where id ?").addEntity(TE…

linux-tar或zip解压缩命令

----------------------------------tar单个文件、文件夹压缩------------------------------------- tar -zcvf /temp/xxx.tar.gz /temp/xxx.txt --压缩 注释&#xff1a;将 /temp/xxx.txt 文件 压缩成 xxx.tar.gz tar -zxvf /temp/xxx.tar.gz -C /temp/ -解压 注释&#…