题目:设计一个类,我们只能生成该类的一个实例。
作用:保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象
关于单例模式,有两种实现方法:饿汉式和懒汉式
饿汉式
优点:加载进程时静态创建单例对象,线程安全
缺点:无论使用与否,总要创建。会造成一定的资源空间浪费
懒汉式
优点:用则创建,不用不创建,什么时候用什么时候创建,空间利用率较高
缺点:在多线程中,首次访问时动态创建单例对象,存在线程不安全的问题
1、不好的解法:只适合单线程
基本思想:只有在m_instance为NULL的时候才创建一个实例,而m_instance是静态属性。同时我们把构造函数定义为私有函数,这样就能确保只有一个实例被创建
#include <iostream>
using namespace std;
class UserManager
{
private: //实现单例常用步骤:
UserManager() //1、构造函数私有化
{
}
public:
static UserManager* GetInstance() //2、写一个静态的函数去创建对象
{
//if (m_instance == NULL) //这里是懒汉式
// m_instance = new UserManager;
handle_count++;
return m_instance;
}
static void Release() //释放
{
if (handle_count >= 1)
handle_count--;
if (m_instance != NULL && handle_count == 0)
{
delete m_instance;
m_instance = NULL;
}
}
private:
static UserManager* m_instance; //3、创建一个静态的指针保存创建的对象
static int handle_count; // 引用计数
};
//UserManager* UserManager::m_instance = NULL; //懒汉式
UserManager* UserManager::m_instance = new UserManager; //饿汉式
int UserManager::handle_count = 0;
int main()
{
UserManager *p1 = UserManager::GetInstance();
UserManager *p2 = UserManager::GetInstance();
if (p1 == p2)
{
cout << "同一个对象" << endl;
}
else
{
cout << "不同对象" << endl;
}
UserManager::Release();
UserManager::Release(); //有几个对象就释放几次
return 0;
}
2、较好的解法:适合多线程
基本思想:如果两个线程同时运行到判断m_instance是否为NULL的if语句,并且m_instance的确没有创建,那么两个线程都会创建一个实例,所以我们需要使用线程互斥锁。当第一个线程上锁时,第二个线程只能等待。当第一个线程发现实例还没有被创建时,它创建一个实例然后解锁,此时第二个线程可以加锁,运行接下来的代码,但是这时候它发现实例已经创建了,所以它直接解锁。这样做有不好的地方,下面在介绍。
#include <iostream>
#include <pthread.h>
using namespace std;
class UserManager
{
private:
UserManager() //1、构造函数私有化
{
cout << "构造函数被调用" << endl;
sleep(5);
}
public:
static UserManager* GetInstance() //2、写一个静态的函数去创建对象
{
if (m_instance == NULL) //注意,为什么if语句判断两次
{
pthread_mutex_lock (&mutex_instance); // 上锁
if (m_instance == NULL)
m_instance = new UserManager;
pthread_mutex_unlock(&mutex_instance); //解锁
}
handle_count++;
return m_instance;
}
static void Release()
{
if (handle_count >= 1)
handle_count--;
if (m_instance != NULL && handle_count == 0)
{
delete m_instance;
m_instance = NULL;
}
}
static void *work (void *) // void *work111(UserManager* tihs, void*)
{
}
void run()
{
pthread_t tid;
pthread_create(&tid, NULL, work, NULL);
}
private:
static pthread_mutex_t mutex_instance;
static UserManager* m_instance; //3、创建一个静态的指针保存创建的对象
static int handle_count; // 引用计数
};
pthread_mutex_t UserManager::mutex_instance = PTHREAD_MUTEX_INITIALIZER;//线程互斥锁初始化
UserManager *UserManager::m_instance = NULL;
int UserManager::handle_count = 0; //类中静态变量在类外初始化
void *work(void *)
{
UserManager* pm = UserManager::GetInstance();
}
int main()
{
pthread_t tid[10];
for (int i = 0; i < 10; i++)
{
pthread_create(&tid[i], NULL, work, NULL); //创建10个线程
pthread_detach(tid[i]); //线程分离
}
pthread_exit(NULL);
return 0;
}
注意:
1、创建多线程的程序是在Linux下运行的。编译:g++ singlon.cpp -lpthread
运行:./a.out
2、运行结果:打印“构造函数被调用”一次,然后沉睡5秒,程序结束。如果不进行加锁解锁操作,结果变为:打印“构造函数被调用”十次,然后沉睡5秒,程序结束。这样的话就不是单例模式了。所以一定要进行加锁解锁操作。
3、代码中为什么要两次使用同样的if语句进行判断?
我们只是在实例还没有创建之前需要加锁操作,以保证只有一个线程创建出实例。而当实例已经创建之后,我们已经不需要再做加锁操作了。而实际上加锁是一个非常耗时的操作,在没有必要的时候我们应该尽量避免。所以,第一个if语句就是用来屏蔽掉那些不需要加锁解锁操作,提高效率,提高代码的质量。
有兴趣的可以关注一下博主的博客: