設計模式 三 單例模式(10分鐘深度搞定)

2021-10-24 18:42:31 字數 4488 閱讀 7646

七、註冊式單例

單例模式是保證任何情況下,都僅有乙個例項,並提供全域性訪問的方法。

先上**

/**

* 餓漢式

*/public class singleton1

public static singleton1 getinstance()

}

餓漢式的關鍵在於instance作為類變數直接得到初始化,該方法能夠百分之百的保證同步,也就是說instance在多執行緒下也不可能被例項化兩次,但是instance被classloader載入後可能很長時間才會被使用,那就意味著instance例項所開闢的空間的堆記憶體會駐留更久的時間。

如果乙個類中的成員屬性比較少,所占用的記憶體資源不多,餓漢式也未嘗不可。總結起來,餓漢式可以保證多執行緒下唯一的例項,getinstance效能也比較高,但是無法進行懶載入

/**

* 懶漢式

*/public class singleton2

public static singleton2 getinstance()

return instance;}}

singleton2 的類變數instance = null,當singleton2.class被初始化的時候instance並不會被例項化,在getinstance方法中會判斷instance 例項是否被例項化,看起來沒什麼問題,但在多執行緒環境下,會導致instance可能被例項化多次。 執行緒1判斷null == instance為true時,還沒有例項化instance,切換到了執行緒2執行,執行緒2判斷null == instance也為true。就會例項化多次。

// 解決執行緒安全問題,但是效率低

public synchronized static singleton2 getinstance2()

return instance;

}

採用懶漢式 + 資料同步方式既滿足了懶載入又能百分之百保證instance例項的唯一性,但是synchronized 關鍵字天生的排他性導致了getinstance方法只能在同一時刻被乙個執行緒所訪問,效能低下。

// 解決執行緒安全問題,提公升效率

private static singleton2 instance = null;

private string msg;

/*** 私有化構造方法

*/private singleton2()

public static singleton2 getinstance3() }}

return instance;

}

當兩個執行緒發現null == instance成立時,只有乙個執行緒有資格進入同步**塊,完成對instance的例項化,隨後的執行緒發現 null == instance 不成立則無須進行任何操作,以後對getinstance的訪問就不需要資料同步的保護了。

這種方式看起來那麼的完美,既滿足了懶載入,***instance例項的唯一性。double-check的方式提供了高效的資料同步策略,可以允許多個執行緒同時對getinstance進行訪問。但是這種方式有可能引起空指標異常,我們分析一下。

private volatile static  singleton2 instance = null;

private string msg;

/*** 私有化構造方法

*/private singleton2()

public static singleton2 getinstance3() }}

return instance;

}

在instance前 加上 volatile的關鍵字,則可以防止重排序的發生。但終歸加了synchronized,對效能依舊造成了影響。有沒有更好的方式呢?有!

public class singleton3 

/*** 私有化構造方法

*/private singleton3()

public static final singleton3 getinstance()

}

在singleton6中並沒有instance的靜態變數,而是將其放在靜態內部類holder類中,因此singleton3初始化過程中並不會建立singleton3的例項,holder類中定義了singleton3的靜態變數,並且直接進行了例項化,當holder被直接引用的時候則會建立singleton3的例項,該方法又是同步方法,保證了記憶體的可見性,jvm的順序性和原子性。holder方式是單例設計最好的設計之一。但是!依然優缺點。

public static void main(string args) throws exception
執行結果

顯然我們建立出來了兩個例項。破壞了我們的初衷。我們來優化一次,在構造方法做限制,一旦重複建立,我們就拋異常。

public class singleton3 

/*** 私有化構造方法

*/private singleton3()

}public static final singleton3 getinstance()

}

感覺上該單例已經完美了,然而還有可能被破壞。

實現序列化

public class singleton4 implements serializable 

/*** 私有化構造方法

*/private singleton4()

}public static final singleton4 getinstance()

}

測試**

singleton4 s1 = null;

singleton4 s2 = singleton4.getinstance();

fileoutputstream fos = new fileoutputstream("singleton4.obj");

objectoutputstream oos = new objectoutputstream(fos);

oos.writeobject(s2);

oos.flush();

oos.close();

fileinputstream fis = new fileinputstream("singleton4.obj");

objectinputstream ois = new objectinputstream(fis);

s1 = (singleton4) ois.readobject();

ois.close();

system.out.println(s1);

system.out.println(s2);

system.out.println(s1 == s2);

執行結果

顯然,單例又遭到破壞。如何解決呢?只需要新增readresolve方法即可。

public class singleton4 implements serializable 

/*** 私有化構造方法

*/private singleton4()

}public static final singleton4 getinstance()

private object readresolve()

}

再看執行效果

有興趣的同學可以檢視jdk的原始碼,發現實際上,這裡我們還是例項化了兩次,只不過第二次建立的物件沒有被返回而已。這樣也會造成記憶體的不必要浪費。

註冊時單例就是將每個例項登記到某個地方,使用唯一標識來獲取例項。

public enum singleton5  

public void setdata(object data)

public static final singleton5 getinstance()

}

驚喜的是,列舉類天生就防止反射破壞與序列化破壞,有興趣的同學,可以查閱jdk原始碼。

列舉類雖然寫法優雅,但是在類載入之時,就將所有物件初始化放在記憶體中,這其實與餓漢式無異。容器式則是將bean 放在concurrenthashmap中,詳細可參照spring ioc的實現,這裡就不多做敘述了。

一分鐘讀懂設計模式 單例模式

單例模式只允許建立乙個物件,因此節省記憶體,加快物件訪問速度。1.需要頻繁例項化然後銷毀的物件。2.建立物件時耗時長,耗資源,又經常用到的物件。3.頻繁訪問資料庫或檔案的物件 4.工具類物件 在類載入的時候不被初始化 不加synchronized執行緒不安全,加synchronized保證執行緒安全...

一分鐘入門設計模式之單例模式

設計模式理論上有 3類23種 定義 單例模式是一種物件建立模式,用於生產乙個物件的例項,它可以確保系統中乙個類只產生乙個例項。單例的好處 單例模式理論上有 5類8種 餓漢模式 靜態常量 優點 寫法簡單,類載入時已經完成了例項化,避免了執行緒同步問題 缺點 會造成記憶體浪費 建立步驟 私有化構造器 本...

設計模式 10 單例模式

單例模式 singleton 簡介 單例模式保證乙個類僅有乙個例項,並提供乙個訪問它的全域性訪問點。使用單例模式能夠讓設計師快速獲取提供某項服務或者功能的物件,可以省去層層傳遞物件的困擾。單例模式在實現時,需要程式語言的支援,需要程式語言具有靜態類屬性 靜態類方法以及可重新定義建構函式的訪問修飾符,...