本文共 7425 字,大约阅读时间需要 24 分钟。
单例模式是保证一个类只有一个实例的一种实现方法,那么怎么实现呢?
1、私有化构造函数。
2、提供一个全局访问方法用于获取实例。
为了有直观的印象,直接上代码:
public class ClassWriteLog{ // 定义一个静态变量来保存类的实例 private static ClassWriteLog clsWriteLog; // 定义一个标识确保线程同步 private static readonly object locker = new object(); // 定义一个静态变量来保存类的实例 // 私有化构造函数 private ClassWriteLog() { } // 定义一个方法获取类的对象 public static ClassWriteLog GetInstance() { // 如果类的实例不存在则创建,否则直接返回 if (clsWriteLog == null) { // 锁定创建对象的过程,如果第一个线程判断对象为空,则创建对象 // 第二个线程到达时,对象也可能为空,那么在锁定的过程中, // 再进行一次判断,如果第一个线程对象创建成功,那么就不用再创建了。 lock (locker) { if (clsWriteLog == null) { clsWriteLog = new ClassWriteLog(); } } } return clsWriteLog; }}
这样通过GetInstance方法就可以保证代码中,获取ClassWriteLog类都获取的是同一个对象而不会重新生成了。
-------该部分引用自 https://www.cnblogs.com/damsoft/p/6105122.html ----------------------------------
那么,为什么要使用单例模式呢?
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,
如多个模块使用同一个数据源连接对象等等。
主要的使用场景有:
1、需要频繁实例化然后销毁的对象。
2、创建对象时好事过多或者资源消耗过多,但又经常用到的对象。
3、有状态的工具类对象。
4、频繁访问数据库活文件的对象。
以下都是单例模式的经典使用场景:
1、资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。 2、控制资源的情况下,方便资源之间的互相通信。如线程池等。 3、有状态的工具类对象。4、频繁访问数据库活文件的对象。
以下都是单例模式的经典使用场景:
1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
2.控制资源的情况下,方便资源之间的互相通信。如线程池等。
应用场景举例:
1.外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:
大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件
2. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,
你能打开两个windows task manager吗? 不信你自己试试看哦~
3. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,
否则内容不好追加。
6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,
主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,
就可以大大降低这种损耗。
8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
10. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,
所有的HttpModule都共享一个HttpApplication实例.
优点:
1.在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例
2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
3.提供了对唯一实例的受控访问。
4.由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
5.允许可变数目的实例。
6.避免对共享资源的多重占用。
缺点:
1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
2.由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
3.单例类的职责过重,在一定程度上违背了“单一职责原则”。
4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
-----------------------------------引用完毕 ------------------------------------------------------
以上这部分内容可以加深对单例模式的理解。其实主要就是两点:
1、单例模式可以减少资源的消耗,避免资源的多重占用;
2、不利于扩展,并且类不适合职责过重;
3、使用共享资源时,建议使用单例模式,避免冲突。
我根据单例模式的学习,修改了之前工作中用到的记录文本日志的类。接着上代码
// 获取日志文件的类,使用了单例模式 public class ClassWriteLog { // 定义一个静态变量来保存类的实例 private static ClassWriteLog clsWriteLog; // 定义一个标识确保线程同步 private static readonly object locker = new object(); //private string FilePath; public FileInfo fi; // 私有化构造函数 private ClassWriteLog() { string FilePath = AppDomain.CurrentDomain.BaseDirectory; // 程序目录下log文件夹 如果不存在则新建 FilePath = AppDomain.CurrentDomain.BaseDirectory + "log"; if (!Directory.Exists(FilePath)) { Directory.CreateDirectory(FilePath); } // log文件夹中按照年和月分开存储 如果不存在则新建 FilePath = FilePath + "\\" + DateTime.Now.ToString("yyMM"); if (!Directory.Exists(FilePath)) { Directory.CreateDirectory(FilePath); } // 根据日期生成不同日期的文件 不存在则生成文件 FilePath = FilePath + "\\" + DateTime.Now.ToString("dd") + ".txt"; if (!File.Exists(FilePath)) { FileStream fs = File.Create(FilePath); fs.Close(); } fi = new FileInfo(FilePath); } // 定义一个方法获取类的对象 public static ClassWriteLog GetInstance() { // 如果类的实例不存在则创建,否则直接返回 if (clsWriteLog == null) { // 锁定创建对象的过程,如果第一个线程判断对象为空,则创建对象 // 第二个线程到达时,对象也可能为空,那么在锁定的过程中, // 再进行一次判断,如果第一个线程对象创建成功,那么就不用再创建了。 lock (locker) { if (clsWriteLog == null) { clsWriteLog = new ClassWriteLog(); } } } return clsWriteLog; } }
// 写入日志的操作类 public class LogWriteOper { static readonly object syncObjectWrite = new object(); public static void Write(object Content) { lock (syncObjectWrite) { // ClassWriteLog.GetInstance()获取单例模式中的对象中的FileInfo类型的属性 using (StreamWriter sw = ClassWriteLog.GetInstance().fi.AppendText()) { sw.Write(Content.ToString() + "\r\n"); sw.Close(); } } } }
下面代码是使用多线程方式调用写入日志的方法,用于测试写入的情况
Thread thread1 = null; for (int iIndex = 0; iIndex < 100; iIndex++) { thread1 = new Thread(new ParameterizedThreadStart(LogWriteOper.Write)); thread1.Start(iIndex.ToString()); } Response.Write("完成");
其实之前写入文本文件日志的时候,没有采用单例模式,也给大家看看,其实使用单例模式真的可以减少资源的消耗,比如我创建文件的那部分内容,
每次写入的时候都要执行,真的蛮耗资源的。
教训啊,同学们。
private static readonly object obj = new object(); ////// 操作日志 /// /// /// /// /// /// /// /// public static void WriteLogs(string strContent) { lock (obj) { string path = AppDomain.CurrentDomain.BaseDirectory;
if (!string.IsNullOrEmpty(path)) { path = AppDomain.CurrentDomain.BaseDirectory + "log"; if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } path = path + "\\" + DateTime.Now.ToString("yyMM"); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } path = path + "\\" + DateTime.Now.ToString("dd") + ".txt"; if (!File.Exists(path)) { FileStream fs = File.Create(path); fs.Close(); } if (File.Exists(path)) { StreamWriter sw = new StreamWriter(path, true, System.Text.Encoding.Default); sw.WriteLine(strContent + "\r\n\r\n"); sw.Close(); } } } }
知行办公,专业移动办公平台 https://zx.naton.cn/
【总监】十二春秋之,3483099@qq.com;
【Master】zelo,616701261@qq.com;
【运营】运维艄公,897221533@qq.com;
【产品设计】流浪猫,364994559@qq.com;
【体验设计】兜兜,2435632247@qq.com;
【iOS】淘码小工,492395860@qq.com;iMcG33K,imcg33k@gmail.com;
【Android】人猿居士,1059604515@qq.com;思路的顿悟,1217022114@qq.com;
【java】首席工程师MR_W,feixue300@qq.com;
【测试】土镜问道,847071279@qq.com;
【数据】fox009521,42151960@qq.com;
【安全】保密,你懂的。
转载地址:https://blog.csdn.net/fox009521/article/details/78485631 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!