........................................单例模式..............................................
单例模式:即唯一实例的意思,也就是保证一个类只有唯一的一个实例,并提供一个访问它的全局访问点。
单例模式的实现方式:
1.需要保证一个类只有一个实例;
在类中,要构造一个实例,就必须调用类的构造函数,为了防止在外部调用类的构造函数而构造实例,需要将构造函数定义为protected或者private。
2.需要提供一个全局访问点;
在类中定义一个该类的static变量,再定义一个获取该static变量的static函数,返回在类内部唯一构造的实例。
以下代码是通过C++实现简单的单例模式(也就是所谓的饿汉式,后面我们会具体分析)
#include<iostream>
using namespace std;
class Singleton
{
public:
//全局访问点
static Singleton* Instance();
int GetTest();
protected:
Singleton();
private:
static Singleton* _instance; //唯一实例
int m_test;
};
Singleton* Singleton::_instance = 0;
Singleton::Singleton ()
{
m_test = 10;
cout<<"Singleton!"<<endl;
}
int Singleton::GetTest ()
{
return m_test;
}
Singleton* Singleton::Instance ()
{
if(_instance == 0)
{
_instance = new Singleton();
}
return _instance;
}
int main()
{
Singleton* sgn = Singleton::Instance ();
cout<<sgn->GetTest ()<<endl;
return 0;
}
在多线程情况下,如果两个线程同时运行到判断_instance是否为NULL的if语句,并且instance的确没有创建时,上述代码就可能创建多个Singleton的实例,在这里为了保证在多线程环境下我们还是只能得到类型的一个实例,需要加上一把同步锁。
如下面代码所示:
#include<iostream>
#include<unistd.h>
#include<pthread.h>
using namespace std;
class Singleton
{
public:
static pthread_mutex_t mutex;
static Singleton* Instance();
int GetTest();
protected:
Singleton();
private:
static Singleton* _instance;
int m_test;
};
Singleton* Singleton::_instance = NULL;
pthread_mutex_t Singleton::mutex;
Singleton::Singleton ()
{
m_test = 10;
cout<<"Singleton!"<<endl;
}
int Singleton::GetTest ()
{
return m_test;
}
Singleton* Singleton::Instance ()
{
if(_instance == NULL)
{
pthread_mutex_lock (&mutex);
if(_instance == NULL)
{
_instance = new Singleton();
}
pthread_mutex_unlock(&mutex);
}
return _instance;
}
int main()
{
Singleton* sgn = Singleton::Instance ();
cout<<sgn->GetTest ()<<endl;
return 0;
}
此处进行了两次_instance==NULL的判断,使用了所谓的“双检锁”机制,因为进行一次加锁和解锁是需要付出相应的代价的。
进行两次判断,可以避免多次加锁与解锁操作,同时也保证了线程安全。
单例模式中的两种实现方式:懒汉式和饿汉式
懒汉:很懒,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化。上面第一个经典单例就是采用懒汉式实现。
饿汉:无论是否调用该类的实例,在单例类定义的时候就进行实例化。
二者选择方式:
- 由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
- 在访问量较小时,采用懒汉实现。这是以时间换空间。
懒汉式需要考虑线程是否安全,这就是我们之前写的第二个代码,需要进行加锁操作。
饿汉式本身就是线程安全的,因为它在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变
饿汉式实现如下:
#include<iostream>
using namespace std;
class Singleton
{
public:
static Singleton* Instance();
int GetTest();
protected:
Singleton();
private:
static Singleton* _instance;
int m_test;
};
Singleton* Singleton::_instance = new Singleton();
Singleton::Singleton ()
{
m_test = 10;
cout<<"Singleton!"<<endl;
}
int Singleton::GetTest ()
{
return m_test;
}
Singleton* Singleton::Instance ()
{
return _instance;
}
int main()
{
Singleton* sgn = Singleton::Instance ();
cout<<sgn->GetTest ()<<endl;
return 0;
}
单例模式的应用:
- Windows的Task Manager(任务管理器)
- Windows的Recycle Bin(回收站)也是典型的单例应用,。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
- 应用程序的日志应用,一般都用单例模式实现。
- Web应用的配置对象的读取,也用单例模式,因为配置文件是共享的资源。
- 数据库连接池的设计也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗。
- 操作系统的文件系统,一个操作系统只能有一个文件系统。
- 多线程的线程池的设计一般也是采用单例模式,是因为线程池要方便对池中的线程进行控制。