保證一個class只有一個實體(Instance),並為它提供一個全域的訪問點(global access point)。
有的時候我們希望某個Class只會有一個Instance被產生。例如,Android中如果多個thread去access同一個DB就會產生錯誤。因此我們可能會希望透過一個Instance統一由他去Access 同一個DB。
在JAVA中實作的方式如下:
public class Singleton {
private volatile Singleton instance = null;
public Singleton getInstance() {
if (instance == null) {
synchronized(this) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
深入探討:為了避免multi-thread 的race condition發生及效率,這裡使用double check locking。避免race condtion的發生所以使用synchronized()。但是synchronized()可能造成performance的低落。因此在外層先判斷如果真的是null才進入critical section否則直接return。
在JDK 4(包含)以下的版本不支援volatile。volatile保證再每次取此變數的值時會去memory抓取而非從cache讀取。不加volatile有可能導致程式碼執行順序被 re-order。進而發生其他thread執行實可能看到非null的instance(但他這個時候明明是null)。
另外,上述的作法是lazy-Initialization的作法。也就是說當真正需要的時候才會被new出來。但是如果我們很確定程式中一定會去new這個物件,其實先把他new 出來也不會有什麼損失。
Singleton without Lazy-Initialization
public class Singleton {
private volatile Singleton instance = new Singleton();
public Singleton getInstance() {
return instance;
}
}
這樣做的另外一個好處 程式碼看起來也比較清爽。
_____________________________________________________________________________________________________________
在C#方式如下:
using System;
public sealed class Singleton
{
private static volatile Singleton instance;
private static object syncRoot = new Object();
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
}
在上面的code中,使用sealed來避免被繼承進而發生可能產生多個instance。另外使用lock的方法來避免multi-thread的race condition。如同在JAVA說的,volatile可以避免out of order所造成的問題。另外,如果不需要lazy-initialization我們可以這樣寫:
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
private Singleton(){}
public static Singleton Instance
{
get
{
return instance;
}
}
}
使用readonly來保證instance只會在static initialization過程時或是在class constructor裡被建立。
_____________________________________________________________________________________________________________
在C++的實作就比較簡單了:
class Singleton {
public:
static Singleton* Instance(){
if (_instance == 0) {
_instance = new Singleton;
}
return _instance;
}
protected:
Singleton();
private:
static Singleton* _instance=0;
}
然而這個作法不是thread safe的。在C++使用如上面提到的Double checked locking方法及使用volatile並不能保證out of order的問題。也就是說在C++上面使用volatile並無法像C#/JAVA上面來保證使用volatile的variable 不會被re-order。因此像下面的DCL的code還是無法保證Thread-Safe。class Singleton {
public :
static Singleton* Instance() {
Lock lock;
if (_instance == 0) {
_instance = new Singleton;
}
return _instance;
}
private :
static Singleton * volatile _instance;
Singleton(){
}
};
最後看到Solstice使用pthread解決這問題,如下:
#includetemplate<typename T> class Singleton : boost::noncopyable { public: static T& instance() { pthread_once(&ponce_, &Singleton::init); return *value_; } static void init() { value_ = new T(); } private: static pthread_once_t ponce_; static T* value_; }; template<typename T> pthread_once_t Singleton<T>::ponce_ = PTHREAD_ONCE_INIT; template<typename T> T* Singleton<T>::value_ = NULL;
Reference:
Gossip@caterpillar Design Pattern: Simple Factory 模式
The "Double-Checked Locking is Broken" Declaration
MSDN:Implementing Singleton in C#
多线程服务器的常用编程模型