單例模式可以保證一個類有且只有一個實例,並提供一個訪問它的全局訪問點.在程序設計中,有很多情況需要確保一個類只能有一個實例.從這句話可以 看出,Singleton模式的核心:如何控制用戶使用new對一個類的實例構造器的任意調用。如何繞過常規的構造器,提供一種機制來保證一個類只有一個 實例?這應該是類設計者的責任,而不是使用者的責任。
一、單例模式意圖
保證一個類有且只有一個實例,並提供一個訪問它的全局訪問點。
二、單例模式UML圖
實現 Singleton 模式的辦法通常有三種:
1.用靜態方法實現 Singleton;
2.以靜態變量爲標誌實現 Singleton;
3.用註冊器機制來創建 Singleton。
三 實現Java Singleton的方案
Java Singleton是指在特定系統範圍內只能實例化一次的Java類,如何理解特定系統範圍?按照需求和環境定義系統範圍,關注在特定系統範圍內單一實例的需求:
系統範圍和環境定義 | 方案編號 |
框架容器內 | A |
單一JVM中、單一類加載器加載類 | B、C、D |
單一JVM、不同類加載器加載相同類 | |
系統跨多個JVM |
A
提供實例管理容器的第三方框架,例如Spring IOC容器,可以通過配置保證實例在容器內的唯一性。這些被外部容器管理的類,能在某個容器範圍內達到Singleton的效果,不一定禁止自身在容器外 部範圍生成多個實例。 這種方案可以看作是一種局部的單例解決方案。
B
代碼示範如下
C
D
有時候類的實例化開銷較爲昂貴,有時候類的實例化要用到系統運行時的動態數據做參數等等,在上述情形,靜態成員變量需要延遲初始化。程序員中間流傳較廣的一種形式是:
這種寫法最大的問題是getInstance()方法被多個線程競爭使用的時候,可能會產生多個實例,違反了單例設計的初衷。如果多個實例的風險(Singleton失敗)不會引起您的系統異常,比如實例存放的是無狀態的數據、實例是輕量級的,您可以堅持這種寫法。
當然還有改進的方法,一般是在該方法前加上“synchronized”關鍵字:
這種作法的副作用就是影響性能。
雙檢查鎖(Double-Checked Locking)是在多線程環境下實現延遲初始化的有效方式(如C++),不幸的是,對大多數JVM是無效的。有一篇文章解釋的很深入:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
其他方案
單一JVM、不同類加載器加載相同類或跨多個JVM的情形,要保證Java Singleton不失敗,我還沒找到恰當的方法。
有些人認爲單例的需求不僅僅是來自某些組件自身的唯一特性,還來自對創建並維護多個對象產生消耗的無法忍受、來自對性能的追求。如果出於降低消耗和 提高性能的目的,很多無狀態的類(類的所有實例天然是始終相同的),即便面臨單一JVM、不同類加載器加載相同類或跨多個JVM的情形,也可以採用上述的 Singleton實現方式,可以儘量減少實例的數量。
當Java Singleton遇到反序列化
一個序列化的實例,每次反序列化的時候都會產生一個新的實例。Singleton也不例外。
我們看看一個例子:
進行序列化測試
public static void main(String[] args) throws Exception{
//序列化
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("E://kingBedroom.obj"));
King king_1 = King.INSTANCE;
objectOutputStream.writeObject(king_1);
objectOutputStream.close();
//反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("E://kingBedroom.obj"));
King king_2 = (King)objectInputStream.readObject();
objectInputStream.close();
//比較是否原來的實例
System.out.println(king_1==king_2);
}
結果是false
解決方法是爲King類增加readResolve()方法:
private Object readResolve(){
return INSTANCE;
}
反序列化之後新創建的對象會先調用此方法,該方法返回的對象引用被返回,取代了新創建的對象。本質上,該方法忽略了新建對象,仍然返回類初始化時創建的那個實例。
五:C++的設計與實現:
Singleton 模式是常用的設計模式之一,但是要實現一個真正實用的設計模式卻也不是件容易的事情。
1. 標準的實現
class Singleton
{
public:
static Singleton * Instance()
{
if( 0 == _instance)
{
_instance = new Singleton;
}
return _instance;
}
protected:
Singleton(void)
{
}
virtual ~ Singleton(void)
{
}
static Singleton* _instance;
};
這是教科書上使用的方法。看起來沒有什麼問題,其實包含很多的問題。下面我們一個一個的解決。
2. 自動垃圾回收
上面的程序必須記住在程序結束的時候,釋放內存。爲了讓它自動的釋放內存,我們引入 auto_ptr 改變它。
#include <memory>
#include <iostream>
using namespace std;
class Singleton
{
public:
static Singleton * Instance()
{
if( 0 == _instance. get())
{
_instance. reset( new Singleton);
}
return _instance. get();
}
protected:
Singleton(void)
{
cout << "Create Singleton" << endl;
}
virtual ~ Singleton(void)
{
cout << "Destroy Singleton" << endl;
}
friend class auto_ptr< Singleton>;
static auto_ptr< Singleton> _instance;
};
//Singleton.cpp
auto_ptr< Singleton> Singleton:: _instance;
3. 增加模板
在我的一個工程中,有多個的 Singleton 類,對 Singleton 類,我都要實現上面這一切,這讓我覺得煩死了。於是我想到了模板來完成這些重複的工作。
現在我們要添加本文中最吸引人單件實現:
/********************************************************************
(c) 2003-2005 C2217 Studio
Purpose: Implement singleton pattern
History:
*********************************************************************/
#pragma once
#include <memory>
using namespace std;
using namespace C2217:: Win32;
namespace C2217
{
namespace Pattern
{
template <class T>
class Singleton
{
public:
static inline T* instance();
private:
Singleton(void){}
~ Singleton(void){}
Singleton(const Singleton&){}
Singleton & operator= (const Singleton &){}
static auto_ptr< T> _instance;
};
template <class T>
auto_ptr< T> Singleton< T>:: _instance;
template <class T>
inline T* Singleton< T>:: instance()
{
if( 0 == _instance. get())
{
_instance. reset ( new T);
}
return _instance. get();
}
//Class that will implement the singleton mode,
//must use the macro in it's delare file
#define DECLARE_SINGLETON_CLASS( type ) /
friend class auto_ptr< type >; /
friend class Singleton< type >;
}
}
4. 線程安全
上面的程序可以適應單線程的程序。但是如果把它用到多線程的程序就會發生問題。主要的問題在於同時執行 _instance. reset ( new T); 就會同時產生兩個新的對象,然後馬上釋放一個,這跟 Singleton 模式的本意不符。所以,你需要更加安全的版本:
/********************************************************************
(c) 2003-2005 C2217 Studio
Purpose: Implement singleton pattern
History:
*********************************************************************/
#pragma once
#include <memory>
using namespace std;
#include "Interlocked.h"
using namespace C2217:: Win32;
namespace C2217
{
namespace Pattern
{
template <class T>
class Singleton
{
public:
static inline T* instance();
private:
Singleton(void){}
~ Singleton(void){}
Singleton(const Singleton&){}
Singleton & operator= (const Singleton &){}
static auto_ptr< T> _instance;
static CResGuard _rs;
};
template <class T>
auto_ptr< T> Singleton< T>:: _instance;
template <class T>
CResGuard Singleton< T>:: _rs;
template <class T>
inline T* Singleton< T>:: instance()
{
if( 0 == _instance. get() )
{
CResGuard:: CGuard gd( _rs);
if( 0 == _instance. get())
{
_instance. reset ( new T);
}
}
return _instance. get();
}
//Class that will implement the singleton mode,
//must use the macro in it's delare file
#define DECLARE_SINGLETON_CLASS( type ) /
friend class auto_ptr< type >; /
friend class Singleton< type >;
}
}
CresGuard 類主要的功能是線程訪問同步 , 代碼如下:
/******************************************************************************
Module: Interlocked.h
Notices: Copyright (c) 2000 Jeffrey Richter
******************************************************************************/
#pragma once
///////////////////////////////////////////////////////////////////////////////
// Instances of this class will be accessed by multiple threads. So,
// all members of this class (except the constructor and destructor)
// must be thread-safe.
class CResGuard {
public:
CResGuard() { m_lGrdCnt = 0 ; InitializeCriticalSection(& m_cs); }
~ CResGuard() { DeleteCriticalSection(& m_cs); }
// IsGuarded is used for debugging
BOOL IsGuarded() const { return( m_lGrdCnt > 0 ); }
public:
class CGuard {
public:
CGuard( CResGuard& rg) : m_rg( rg) { m_rg. Guard(); };
~ CGuard() { m_rg. Unguard(); }
private:
CResGuard& m_rg;
};
private:
void Guard() { EnterCriticalSection(& m_cs); m_lGrdCnt++; }
void Unguard() { m_lGrdCnt--; LeaveCriticalSection(& m_cs); }
// Guard/Unguard can only be accessed by the nested CGuard class.
friend class CResGuard:: CGuard;
private:
CRITICAL_SECTION m_cs;
long m_lGrdCnt; // # of EnterCriticalSection calls
};
///////////////////////////////////////////////////////////////////////////////
5. 實用方法
比如你有一個需要實現單件模式的類,就應該這樣實現:
#pragma once
#include "singleton.h"
using namespace C2217:: Pattern;
class ServiceManger
{
public:
void Run()
{
}
private:
ServiceManger(void)
{
}
virtual ~ ServiceManger(void)
{
}
DECLARE_SINGLETON_CLASS( ServiceManger);
};
typedef Singleton< ServiceManger> SSManger;
在使用的時候很簡單,跟一般的 Singleton 實現的方法沒有什麼不同。
int _tmain(int argc, _TCHAR* argv[])
{
SSManger:: instance()-> Run();
}