很久没有在CSDN上写笔记了,这次写一下比较常见的单例模式。
单例模式的所有实现的第一条就是必须将类的构造函数设置为私有,使得其他类没有变法直接调用构造函数生产对象。先看最简单的一种:
public sealed class Singleton
{
public static Singleton instance = null;
private Singleton() { }
public static Singleton Instance
{
get
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
}
实现很简单,理解起来也不难,但是有个致命的缺点:线程不安全,两个线程可能同事检测到 instance==null成立,因为有可能在一瞬间创建两个。其实在多线程运行时,即使已经在内存中创建了实例,但是内存模型并不能保证新的instance能够看见其它线程所创建的实例。因此这种方法不推荐。下面看看线程安全的方法:
public sealed class Singleton2
{
private static Singleton2 instance = null;
private static readonly object padlock = new object();
private Singleton2() { }
public static Singleton2 Instance
{
get
{
lock(padlock)
{
if (instance == null)
instance = new Singleton2();
return instance;
}
}
}
}
变化不大,基于前面的分析,这个只是添加了一个object成员以此来实现锁。这种方法是线程安全的,每个线程要创建实例时,都要先去得锁,然后再判断是否为空,也就是保证多线程运行时,只有一个线程能进行进行实例化,而一旦一个线程实例化后,后面的线程取到锁后,就会发现实例已经创建。但是这个方法也有一个明显的缺点:假设线程足够多,100个吧,每一个线程在进行if(instance==null)判断时,都到等到某一个线程退出后将锁获得。所以会导致性能问题。改进的办法就是多进行以此判断,如果instance==null是FALSE,那自然没必要在等待锁,再去判断一次。
public sealed class Singleton2
{
private static Singleton2 instance = null;
private static readonly object padlock = new object();
private Singleton2() { }
public static Singleton2 Instance
{
get
{
if(null==instance)
{
lock (padlock)
{
if (instance == null)
instance = new Singleton2();
}
}
return instance;
}
}
}
这种方式主要利用了C#的“静态初始化方法”,这种方法不需要你显示的编写线程安全的代码,就可以保证线程安全。
public sealed class Singleton3
{
private static readonly Singleton3 instance = new Singleton3();
static Singleton3() { }
private Singleton3() { }
public static Singleton3 Instance
{
get
{
return instance;
}
}
}
正如你看到的,这个似乎比前面两种实现都要简单,它是如何保证线程安全的呢?对于每个AppDomain,在C#中类的实例被构造或者静态成员被引用时,静态构造函数才会执行,而且只执行一次。由于这个类在一加载就被实例化,因此,效率上要比前面实现高的多。
那为何叫饿汉模式呢?这是因为相对于第2、3的实现方式,Singleton必选被引用一次后才会将自己实例化,这种成为懒汉式;而当下,无论我们在程序中用不用,编译器都会实例化一个对象,所以就成为“饿汉模式”
那我们看另外一种懒汉模式:
public sealed class Singleton
{
private Singleton()
{
}
public static Singleton Instance { get { return Nested.instance; } }
private class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
当嵌套类的静态成员第一次被引用到时,就会触发实例化,因此这是一种懒汉式单例模式。但是有着前面所有模式的有点,因此相当推荐!请注意,尽管嵌套类可以访问封闭类的私有成员,但情况并非如此,因此这里实例需要是内部的。不过,这不会引起任何其他问题,因为类本身是私有的。然而,为了使实例化变得懒惰,代码要复杂一些。
在.net4之后,可以使用System.Lazy
public sealed class Singleton
{
private static readonly Lazy lazy =
new Lazy(() => new Singleton());
public static Singleton Instance { get { return lazy.Value; } }
private Singleton()
{
}
}
上面的代码会隐式的调用LazyThreadSafetyMode.ExecutionAndPublication以实现lazy