设计模式--单例模式(Singleton)

本文介绍了单例模式的概念、实现方式及应用场景。详细解释了懒汉式与饿汉式的区别,并给出了具体的C++实现示例。此外,还探讨了在多线程环境中如何保证单例模式的线程安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

........................................单例模式..............................................


单例模式即唯一实例的意思,也就是保证一个类只有唯一的一个实例,并提供一个访问它的全局访问点

单例模式的实现方式:

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应用的配置对象的读取,也用单例模式,因为配置文件是共享的资源。
  • 数据库连接池的设计也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗。
  • 操作系统的文件系统,一个操作系统只能有一个文件系统。
  • 多线程的线程池的设计一般也是采用单例模式,是因为线程池要方便对池中的线程进行控制。
根据以上,可以发现单例模式应用的条件是
      
       1.资源共享的情况下,避免由于资源操作时导致的性能或损耗,例如日志文件,应用配置。
        2.控制资源的情况下,方便资源之间的互相通信,如线程池。
 














评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值