个人理解:在代码运行期间使用单例模式实现的部分有且只有一个实例存在。
应用场景:一般配置文件读取,日志记录,还有部分数据库访问连接池等都可以设计成单例模式。
几种实现方式:
这里以读取配置文件为例
一个配置文件对象:
public class CustomConfig
{
public string EmailUserName { get; set; }//邮箱账号
public string EmailContentModel { get; set; }//邮箱内容模板
public string EmailPassword { get; set; }//邮箱密码
}
config配置文件:
单例模式一般分为饿汉懒汉两种方式。
饿汉模式在类被加载时就将自己实例化,它的优点在于无须考虑多个线程同时访问的问题,可以确保实例的唯一性;从调用速度和反应时间角度来讲,由于单例对象一开始就得以创建,因此要优于懒汉式单例。但是无论系统在运行时是否需要使用该单例对象,由于在类加载时该对象就需要创建,因此从资源利用效率角度来讲,饿汉式单例不及懒汉式单例,而且在系统加载时由于需要创建饿汉式单例对象,加载时间可能会比较长。
先看饿汉。
public sealed class SingletonFourth_1
{
private static readonly CustomConfig Instance = new CustomConfig();
public static CustomConfig GetInstance()
{
return Instance;
}
private SingletonFourth_1()//禁止实例化
{
}
}
public sealed class SingletonFourth_2
{
private static readonly CustomConfig Instance = null;
static SingletonFourth_2()
{
Instance = new CustomConfig();
}
private SingletonFourth_2() { }//禁止外部实例化
public static CustomConfig GetInstance()
{
return Instance;
}
}
这两个只是写法不同,但是其实都是一样的。
懒汉模式在第一次使用时创建,无须一直占用系统资源,实现了延迟加载,但是必须处理好多个线程同时访问的问题,这将导致系统性能受到一定影响。
public sealed class SingletonFirst
{
private static CustomConfig _instance = null;
public static CustomConfig Instance
{
get
{
if (_instance != null) return _instance;
_instance = new CustomConfig();
return _instance;
}
}
private SingletonFirst()//禁止外部实例化
{
}
}
这是一个简单的懒汉,这种写法在单线程的时候不会出现太大的问题,但多个线程同时访问的时候就有可能同时创建多个实例,而且这多个实例不是同一个对象,虽然后创建的实例会覆盖先创建的实例,但是还是会存在拿到不同对象的情况。
public sealed class SingletonSecond
{
private static CustomConfig _instance = null;
private static readonly object PadLock = new object();
public static CustomConfig Instance
{
get
{
lock (PadLock)
{
if (_instance != null) return _instance;
_instance = new CustomConfig();
return _instance;
}
}
}
private SingletonSecond()//禁止外部实例化
{
}
}
这种写法线程安全了,不过当两个线程同时访问的时候,还是会实例化两次,这样会产生不必要的消耗。
public sealed class SingletonThird
{
private static CustomConfig _instance = null;
private static readonly object PadLock = new object();
public static CustomConfig GetInstance()
{
if (_instance != null) return _instance;//这里检查是避免资源浪费,实例不存在才进入同步模块
{
lock (PadLock)
{
if (_instance != null) return _instance;//同步模块再检查一次,如果不存在,就只需要同步创建一次
_instance = new CustomConfig();
_instance.EmailUserName = System.Configuration.ConfigurationManager.AppSettings["EmailUserName"];
_instance.EmailPassword = System.Configuration.ConfigurationManager.AppSettings["EmailPassword"];
_instance.EmailContentModel = System.Configuration.ConfigurationManager.AppSettings["EmailContentModel"];
return _instance;
}
}
}
private SingletonThird()//禁止外部实例化
{
}
}
这种方式是平时看到比较多的双重校验的方式了,在进入锁之前先检查一次。这种方式资源利用率比较高,但是如果是遇到大量操作,加锁会成为性能瓶颈。
public sealed class SingletonFifth
{
private static class Inside
{
internal static readonly CustomConfig Instance = new CustomConfig()
{
EmailUserName = System.Configuration.ConfigurationManager.AppSettings["EmailUserName"],
EmailPassword = System.Configuration.ConfigurationManager.AppSettings["EmailPassword"],
EmailContentModel = System.Configuration.ConfigurationManager.AppSettings["EmailContentModel"]
};
}
public static CustomConfig GetInstance() { return Inside.Instance; }
private SingletonFifth()//禁止外部实例化
{
}
}
这种方式是通过静态内部类的方式实现的,在性能要求比较高时,就可以使用这种方式,从而避免频繁的加锁和解锁造成的资源浪费。
public sealed class SingletonSixth
{
private static readonly Lazy<CustomConfig> Lazy =
new Lazy<CustomConfig>(() => new CustomConfig()
{
EmailUserName = System.Configuration.ConfigurationManager.AppSettings["EmailUserName"],
EmailPassword = System.Configuration.ConfigurationManager.AppSettings["EmailPassword"],
EmailContentModel = System.Configuration.ConfigurationManager.AppSettings["EmailContentModel"]
});
public static CustomConfig GetInstance()
{
return Lazy.Value;
}
private SingletonSixth()//禁止外部实例化
{
}
}
这种方式是通过.net4.0新增的Lazy<T>的类,它提供了线程安全的延迟加载的调用。
以上就是这两天了解到的单例模式实现方式,记下来防止以后找不到了。
参考:http://csharpindepth.com/Articles/General/Singleton.aspx#unsafe
http://www.cnblogs.com/wuchanming/p/4486357.html
http://cantellow.iteye.com/blog/838473