一、定义
这种模式设计到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
总结为三点:
(1)单例类只能有一个实例
(2)单例类必须自己创建自己的唯一实例
(3)单例类必须给所有其他对象提供这一实例
目的:保证一个类仅有一个实例,并提供一个访问它的全局访问点
主要解决:一个全局使用的类频繁地创建与销毁
何时使用:当你想控制实例数目,节省系统资源的时候
实现思路:判断系统是否已经有这个单例,如果有则返回,如果没有则创建
注意点:构造函数是私有的
应用场景举例:
1、一个党只能有一个主席
2、Windows是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
3、一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
实际工作中常见的应用举例
1、日志类:一个应用往往只对应一个日志实例
2、配置类:应用的配置集中管理,并提供全局访问
3、管理器:比如windows系统的任务管理器就是一个例子,总是只有一个管理器的实例
4、共享资源类:加载资源需要较长时间,使用单例可以避免重复加载资源,并被多个地方共享访问。
二、实现
结构图:
Singleton |
-instance:Singleton |
-Singleton() +GetInstance() |
说明:Singleton类,定义一个GetInstance操作,允许客户访问它的唯一实例。GetInstance是一个静态方法,主要负责创建自己的唯一实例。
(1)懒汉模式
Singleton在程序第一次调用的时候才会初始化。
---------线程不安全-------
class Singleton{
private:
static Singleton* instance;
Singleton(){}
public:
static Singleton* GetInstance(){
if(NULL == instance){
instance = new Singleton();
}
return instance;
}
};
Singleton*Singleton::instance = NULL; //静态数据成员是静态存储的,必须对他进行初始化
上面的模式存在一个问题,当多个线程并行调用GetInstance()的时候,就会创建多个实例,也就是说在多线程下不能正常工作。
下面进行改进,引入双重校验锁。
----------线程安全--------
class Singleton{
private:
static Singleton* m_instance;
Singleton(){}
public:
static Singleton* GetInstance();
};
Singleton*Singleton::GetInstance(){
if(NULL == m_instance){
Lock(); //借用其他类来实现,如boost
if(NULL == m_instance){
m_instance = new Singleton();
}
UnLock();
}
return m_instance;
}
lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待。直到该对象被释放。
如果处理大量数据时,锁会成为整个性能的瓶颈。一般懒汉模式适用于程序一部分中需要使用Singleton,且在实例化后没有大量频繁访问或线程访问的情况。
(2)饿汉模式
无论是否调用该类的实例,在程序开始时就会产生一个该类的实例,并在以后仅返回此实例。
由静态初始化实例保证其线程安全性。因为静态实例初始化在程序开始时进入主函数之前就由主线程以单线程方式完成了初始化,不必担心多线程问题。
故在性能需求较高时,应使用这种模式,避免频繁的锁争夺。
classSingletonStatic{
private:
static const SingletonStatic* m_instance;
SingletonStatic(){}
public:
static const SingletonStatic*GetInstance(){
return m_instance;
}
};
constSingletonStatic* SingletonStatic::m_instance = new SingletonStatic();
三、总结
静态初始化的方式是在自己被加载时就将自己实例化,所以被形象地称之为饿汉式单例类。之前的处理方式是要在第一次被引用时,才会将自己实例化,所以就被称为饿汉式单例类。