常用设计模式学习-单例模式
个人理解:在代码运行期间使用单例模式实现的部分有且只有一个实例存在。
应用场景:一般配置文件读取,日志记录,还有部分数据库访问连接池等都可以设计成单例模式。
几种实现方式:
这里以读取配置文件为例
一个配置文件对象:
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