1. 程式人生 > >設計模式--單例模式(Singleton)

設計模式--單例模式(Singleton)

........................................單例模式..............................................

單例模式即唯一例項的意思,也就是保證一個類只有唯一的一個例項,並提供一個訪問它的全域性訪問點

單例模式的實現方式:

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.控制資源的情況下,方便資源之間的互相通訊,如執行緒池。