方法的提煉和重要,同樣的代碼加入出現了5遍,只要我們稍微一修改,我們要修改的就是5個地方,可能我們有時候改了幾個地方,有1、2個地方漏改了,方法的提煉有助於開發的維護和擴展。好吧,扯遠了,回到我們的正題,單例模式。
一、定義:
單例模式(Singleton),保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
通常我們可以讓一個全局變量使得一個對象被訪問,但它不能防止你的實例化多個對象,一個最好的方法就是,讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例可以被創建,並且它可以提供一個訪問該實例的方法。 通俗的說就是, 將構造函數聲明為private,防止外部類的實例化,提供一個public的方法用於生成對象。
二、UML圖
名詞解釋:
lazy Loading:延遲加載,即在需要的時候載加載類,而不是一開始就加載。
三、常用方法形式
方式1、將構造函數聲明為private,提供一個公共的外界調用該對象的方式(lazy Loading,線程不安全)
public class Singleton{
private static Singletoninstance=null;
private Singleton(){ //構造方法是private,堵死了外界用new實例化
//dosomething
}
//獲取本類實例的唯一全局訪問點
public static Singleton getInstance(){
if(instance==null){ //若實例不存在,創建一個返回,否則返回已有實例
instance=new Singleton();
}
return instance;
}
}
主要缺點:在線程下工作容易出問題,生成多個對象。
方式2、(lazy Loading,線程不安全)
public class Singleton{
private static Singletoninstance=null;
private Singleton(){ //構造方法是private,堵死了外界用new實例化
//dosomething
}
//獲取本類實例的唯一全局訪問點
public static synchronized Singleton getInstance(){
if(instance==null){ //若實例不存在,創建一個返回,否則返回已有實例
instance=new Singleton();
}
return instance;
}
}
能很好的解決上面說的多線程問題,但是使多線程的變成了單線程的效率,效率很差。一般不適用。
方式3、在類裡面生成唯一的對象,每次生成該對象都是返回那唯一的對象。(非Lazy Loading)
public class Singleton{
//在自己內部定義自己的一個實例,只供內部調用
private static Singleton instance=new Singleton();
private Singleton(){
//dosomething
}
//這裡提供了一個供外部訪問本class的靜態方法,可以直接訪問
public static Singleton getInstance(){
return instance;
}
}
不是延遲加載類型,類在類加載的時候就進行了初始化,這樣子會浪費一定的知源,違背了延遲加載的性質。
方式4
public class Singleton{
private static Singletoninstance=null;
private Singleton(){
//dosomething
}
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(null==instance){
instance=new Singleton();
}
}
}
return instance;
}
}
//這個模式將同步內容下方到if內部,提高了執行的效率,不必每次
獲取對象時都進行同步,只有第一次才同步,創建了以後就沒必要了。
通過靜態代碼塊(static)的方式,將對象生成延遲了類初始化的時候。
方式5、雙重鎖機制
public class Singleton
{
//定義一個私有的靜態全局變量來保存該類的唯一實例
private static Singleton singleton;
//定義一個只讀靜態對象
//且這個對象是在程序運行時創建的
private static readonly object syncObject = new object();
/// <summary>
/// 構造函數必須是私有的
/// 這樣在外部便無法使用 new 來創建該類的實例
/// </summary>
private Singleton()
{}
/// <summary>
/// 定義一個全局訪問點
/// 設置為靜態方法
/// 則在類的外部便無需實例化就可以調用該方法
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
//這裡可以保證只實例化一次
//即在第一次調用時實例化
//以後調用便不會再實例化
//第一重 singleton == null
if (singleton == null)
{
lock (syncObject)
{
//第二重 singleton == null
if (singleton == null)
{
singleton = new Singleton();
}
}
}
return singleton;
}
}
解析:在外層已經判斷了singleton == null,為什麼在lock()後面還需要判斷一次singleton == null呢?
因為,加入有兩個線程同時調用了getInstance()方法,它們將都可以通過第一重singleton == null的判斷,然後由於lock機智,這兩個線程則一個進入,另一個在排隊等待,必須要其中的一個進入並出來後,另外一個才能進入,因此如果沒有了第二重instance是否為空的判斷,則第一個線程創建了實例,而第二個線程還是可以繼續再創建新的實例,這就沒有達到單例的目的了。