单例模式是保证一个类只有一个实例的一种实现方法,那么怎么实现呢?
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();
/// <summary>
/// 操作日志
/// </summary>
/// <param name="level"></param>
/// <param name="time"></param>
/// <param name="user"></param>
/// <param name="browser"></param>
/// <param name="ipAdrass"></param>
/// <param name="operate"></param>
/// <param name="detail"></param>
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;
【安全】保密,你懂的。