單例模式,是一種經常使用的軟件設計模式。在它的核心結構中只包含一個被稱爲單例的特殊類。經過單例模式能夠保證系統中,應用該模式的一個類只有一個實例。即一個類只有一個對象實例。git
一、單例類只能有一個實例。
二、單例類必須本身建立本身的惟一實例。
三、單例類必須給全部其餘對象提供這一實例github
說到代碼實現,主要是3點
1.私有靜態變量
2.私有化構造函數
3.靜態的對象獲取方法
相信看到這的時候你內心就有個答案了,看看和下面的所謂懶漢模式的代碼是否是同樣。設計模式
/// <summary> /// 懶漢,線程不安全 /// </summary> public class Singleton1 { private static Singleton1 _singleton = null;//私有靜態變量 private Singleton1() { }//私有化構造函數 public static Singleton1 GetSingleton()//靜態的對象獲取方法 { if (_singleton == null)//保證爲空才建立 { _singleton = new Singleton1(); } return _singleton; } }
若是這個答案和你想的同樣,並一直也是這樣用的,那隻能說too young to simple,此方法其實有倆方面的問題,首先是線程不安全,若是同一時間,多個線程同時獲取實例,則會出現獲取到不一樣的實例,所謂的單例也就成了線程內單例,明顯不對,解決辦法往下看。安全
/// <summary> /// 懶漢,線程安全 /// </summary> public class Singleton2 { private static Singleton2 _singleton = null; private static object _lock = new object(); private Singleton2() { } public static Singleton2 GetSingleton() { lock (_lock)//保證線程安全 { if (_singleton == null)//保證爲空才建立 { _singleton = new Singleton2(); } return _singleton; } } }
話很少說,看過代碼的你多半你也會說一句,加鎖大法好.到這咱們基本上算是實現了,可是咱們看看這個鎖,雖然解決了多個實例對象問題,可是該方式運行效率卻很低,下一個線程想要獲取對象,就必須等待上一個線程釋放鎖以後,才能夠繼續運行,但實際上對象大部分狀況是已經存在了的並不須要new,所以對上個方法作了個優化。函數
/// <summary> /// 懶漢優化 /// </summary> public class Singleton3 { private static Singleton3 _singleton = null; private static object _lock = new object(); private Singleton3() { } public static Singleton3 GetSingleton() { if (_singleton == null)//保證對象初始化以後,線程不須要等待鎖 { lock (_lock)//保證線程安全 { if (_singleton == null)//保證爲空才建立 { _singleton = new Singleton3(); } } } return _singleton; } }
到這咱們想要的單例模式基本算ok了,碼得我本身都累,想一想設計模式這麼多年的沉澱,就沒有簡單點的寫法麼?答案固然是確定有的,從實現方法的命名也能夠看出,除了懶漢模式,還有對應的餓漢模式,相對來講寫法比較簡單。優化
/// <summary> /// 餓漢模式1 /// </summary> public class Singleton4 { private static Singleton4 _singleton = null; private Singleton4() { } /// <summary> /// 靜態構造函數,由CLR調用,在使用以前被調用,並且之調用一次 /// </summary> static Singleton4() { _singleton = new Singleton4(); } public static Singleton4 GetSingleton() { return _singleton; } } /// <summary> /// 餓漢模式2 /// </summary> public class Singleton5 { /// <summary> /// 靜態變量:會在類型第一次使用的時候初始化,並且只初始化一次 /// </summary> private static Singleton5 _singleton = new Singleton5(); private Singleton5() { } public static Singleton5 GetSingleton() { return _singleton; } }
以上倆種方式,我我的認爲是等價的,一個應用了靜態變量,一個用的靜態構造函數。雖然寫法相對簡單,不過,instance 在類裝載時就實例化,雖然致使類裝載的緣由有不少種,在單例模式中大多數都是調用 getInstance 方法, 可是也不能肯定有其餘的方式(或者其餘的靜態方法)致使類裝載,這時候初始化 instance 顯然沒有達到 lazy loading 的效果。嗨,有問題總得解決,看看下面。線程
public class Singleton6 { private static class SingletonHolder { internal static Singleton6 _singleton = new Singleton6(); } private Singleton6() { } public static Singleton6 GetSingleton() { return SingletonHolder._singleton; } }
這種方式一樣利用了靜態變量的機制來保證初始化 instance 時只有一個線程,同時又解決了lazy loading。由於 SingletonHolder 類沒有被主動使用,只有經過顯式調用 getInstance 方法時,纔會顯式裝載 SingletonHolder 類,從而實例化 instance。想象一下,若是實例化 instance 很消耗資源,因此想讓它延遲加載,另一方面,又不但願在 Singleton 類加載時就實例化,這個時候,這種方式相對來講比較實用。設計
通常狀況下,懶漢優化模式更多的是讓咱們從頭出發,理解單例模式的運行過程和實現思路,建議使用餓漢方式(Singleton4/Singleton5)。只有在要明確實現 lazy loading 效果時,纔會使用登記模式。code
事實上,經過反射機制是可以實例化構造方法爲private的類的,那基本上會使全部的單例實現失效。因此咱們這裏不考慮反射的狀況
下載源碼對象