[白话设计模式] Singleton

现实生活中有很多唯一的概念,如: 一个联合国秘书长,一个中国,一夫一妻制等等,多了怕要出问题.

我们知道对象都是New出来的,要保证对象的唯一关键在于控制对象的创建权.所谓只允许官家放火,不许百姓点灯.官家只有一个,可以负责创建,要是随随便便让百姓点把火,怕是到处要遭火灾了.

public class Singleton
{
    private Singleton() {}
}

保证了类不能被client创建.那问题来了,client怎么访问呢,官方负责创建,并提供访问的入口.

private static Singleton m_instance = new Singleton();
public static Singleton Instance
{
    get { return m_instance; }
}

此为"饿汉式单例类",因为此类一般加载就会调用类的静态构造函数,创建了类的实例.

相比有"懒汉式单例类",

public class Singleton
{
    private static Singleton m_instance = null;
    private Singleton() { }
    public static Singleton Instance
    {
        get
        {
            if (m_instance == null)
                m_instance = new Singleton();
            return m_instance;
        }
    }
}

改进的地方在于延迟了类的创建,但相比"饿汉式单例类"确有致命弱点,存在线程安全问题.编译器能够保证静态构造函数只被调用一次,所以"饿汉式单例类"是没有这个问题的.

改进方法很简单,加个锁.

private static object m_locker = new object();

get
{
    lock (m_locker)
    {
    if (m_instance == null)
        m_instance = new Singleton();
    return m_instance;
}

注意此锁对象必须为static,因为要保证在调用Instance方法前,此对象就被创建完毕.

有人说,这样不是类在加载的时候也需要创建了一个静态变量实例吗.对,你观察的很仔细,但是你要知道一个object实例对象的创建一般要比Singleton实例的创建开销小的多.

此加锁方法仍然存在严重的性能问题,要知道Singleton创建只有一次,但却可能被多线程频繁访问,访问一下加一下锁,怎么能保证并发访问的效率呢?

这时候双重锁,派上了用场了.

if (m_instance == null)
{
    lock (m_locker)
    {
        if (m_instance == null)
            m_instance = new Singleton();
    }
}
return m_instance;

这样就是一个比较完美的解决方案了,既做到了延迟创建,又保证了多线程安全,同时也保证了效率.只有第一次对象为null时,要加次锁,开销不大.

最后要注意的地方就是在lock段里面还是要判断m_instance是否为null,要不然在并发的时候还是能创建多个对象的哦.

 

实际应用:

经常应用于读取配置文件啥的,一些Manager类啥的

和工厂模式一起使用,class loader等

Remoting Server激活方式有种就是Singleton

 

参考

阎宏,《Java与模式》

Terrylee .NET设计模式(2):单件模式(Singleton Pattern)

posted @ 2007-06-26 15:53  Anders06  阅读(834)  评论(2编辑  收藏  举报