保證一個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#
多线程服务器的常用编程模型