设计模式----单例模式

一、单例模式

一般情况下,我们建立的一些类是属于工具性质的,基本不用存储太多的跟自身有关的数据,在这种情况下,每次都去new一个对象,即增加了开销,也使得代码更加臃肿。其实,我们只需要一个实例对象就可以。如果采用全局或者静态变量的方式,会影响封装性,难以保证别的代码不会对全局变量造成影响。
考虑到这些需要,我们将默认的构造函数声明为私有的,这样就不会被外部所new了,甚至可以将析构函数也声明为私有的,这样就只有自己能够删除自己了。这就是所谓的单例模式啦。

1、基本概念

数学与逻辑学中,singleton定义为“有且仅有一个元素的集合”。
单例模式最初的定义出现于《设计模式》(艾迪生维斯理, 1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”
通俗来讲,所谓单例模式就是一个类只有一个实例

2、单例模式的特点

(1)单例类确保自己只有一个实例
(2)单例类必须自己创建自己的实例
(3)单例类必须为其他对象提供唯一的实例

二、单例模式的几种实现方式

1、懒汉式(线程不安全)

构造函数声明为private或protect防止被外部函数实例化,内部保存一个private static的类指针保存唯一的实例,实例的动作由一个public的类方法代劳,该方法也返回单例类唯一的实例。
代码实现:

class singleton
{
protected:
    singleton(){}
private:
    static singleton* p;
public:
    static singleton* instance();
};
singleton* singleton::p = NULL;
singleton* singleton::instance()
{
    if (p == NULL)
        p = new singleton();
    return p;
}

这个方法实现起来,简单易懂,但是存在一个致命的不安全因素,该方法是线程不安全的,考虑两个线程同时首次调用instance方法且同时检测到p是NULL值,则两个线程会同时构造一个实例给p,这是严重的错误!所以该方法只适用於单线程方法。

2、懒汉式(线程安全)

为了解决上面出现的问题,可以有以下两种方法解决:
加锁的经典懒汉模式:

class singleton
{
protected:
    singleton()
    {
        pthread_mutex_init(&mutex);
    }
private:
    static singleton* p;
public:
    static pthread_mutex_t mutex;
    static singleton* initance();
};

pthread_mutex_t singleton::mutex;
singleton* singleton::p = NULL;
singleton* singleton::initance()
{
    if (p == NULL)
    {
        pthread_mutex_lock(&mutex);
        if (p == NULL)
            p = new singleton();
        pthread_mutex_unlock(&mutex);
    }
    return p;
}

这种双重锁定的方式,不仅避免了大量线程同时堵塞,而且又加上了一道防御if (p == NULL),这样就确保不会重复创建了.
内部静态变量的懒汉实现:
在instance函数里定义一个静态的实例,也可以保证拥有唯一实例,在返回时只需要返回其指针就可以了。推荐这种实现方法。

class singleton
{
    protected:
        singleton()
        {
            pthread_mutex_init(&mutex);
        }
    public:
        static pthread_mutex_t mutex;
        static singleton* initance();
};

pthread_mutex_t singleton::mutex;
singleton* singleton::initance()
{
    pthread_mutex_lock(&mutex);
    static singleton obj;
    pthread_mutex_unlock(&mutex);
    return &obj;
}

3、饿汉式(以空间换时间)

饿汉:饿了肯定要饥不择食,所以在单例类定义的时候就进行实例化。
代码实现如下:

class singleton
{
    protected:
        singleton()
        {}
    private:
        static singleton* p;
    public:
        static singleton* initance();
};
singleton* singleton::p = new singleton;
singleton* singleton::initance()
{
   return p;
}

那么针对以上方式如何选择呢:

  • 在单线程中,不存在线程安全时,可以采用懒汉式(线程不安全)
  • 由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
  • 在访问量较小时,采用懒汉(线程安全)实现。这是以时间换空间。

三、单例模式的主要优缺点

1、优点

1、提供了对唯一实例的受控访问。

2、由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。

3、允许可变数目的实例。

2、缺点

1、由於单利模式中没有抽象层,因此单例类的扩展有很大的困难。

2、单例类的职责过重,在一定程度上违背了“单一职责原则”。

3、滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

发布了115 篇原创文章 · 获赞 74 · 访问量 8万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章