Android重拾設計模式系列——單例模式的5種實現
封面-設計模式.png
單例模式是我們最常使用,也是最簡單的一種模式,主要用于只想系統中存在一個實例的情況,比如某個Manager。
定義及實質
-
定義
確保某一個類只有一個實例,而且自行實例化并向系統提供這個實例。
-
實質
控制實例數量,確保只有一個實例。
模式圖解
單例模式UML圖
單例模式UML圖
很直觀明了,很簡單。下面來看看單例模式的不同實現方案。
餓漢式
public class Singleton{
private static fianl Singleton instance = new Singleton();
//私有化構造器,避免外部訪問。使用反射仍然可以訪問,所以安全是相對的。
//但仍然可以通過哈希值等進行限制,提高安全性。
priavte Singleton{
}
//對外暴露的接口,用于獲取實例
public static Singleton getInstance(){
return instance;
}
public void doSomething(){
System.out.println("doSomething");
}
}
解釋:
- 餓漢式是利用了 static 關鍵字在類加載時就會進行初始化,并且緩存到 靜態內存 中的特點,確保了調用 getInstance() 時,無須擔心 instance 為null;
- 通過 fianl 關鍵字,式單例在多線程情況下的安全,因為JVM會自動對 fianl 進行上鎖同步。
優點:能夠在線程安全的情況下實現單例。
缺點:由于類一加載就會創建實例,所以會較早占用系統資源。
懶漢式
public class Singleton{
private static Singleton instance;
priavte Singleton{
}
//加synchronized上鎖,可以一定程度上確保安全性
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
public void doSomething(){
System.out.println("doSomething");
}
}
解釋:
- 懶漢式體現了延遲加載的的思想。對象實例只有在第一次調用 getInstance() 方法時才會被創建,一定程度上的節約了系統資源;
- 懶漢式在單線程下能夠很好的工作,但是并發下就很有可能會創建多個實例。
優點:能夠實現延遲加載,節約內存。在單線程中能很好工作。
缺點:并發下可能會創建多個實例,每次判斷都會耗費一些時間。
DCL雙重檢查實現單例
public class Singleton{
//這里使用了volatile關鍵字,它能夠確保insatnce變量每次都直接從主內存(而不是寄存器)中加載最新賦值。
private volatile static Singleton instance = null;
priavte Singleton{
}
//這里進行了兩次null檢查,即雙重檢查鎖定,這能很大程度的確保安全性
public static Singleton getInstance(){
if(instance == null){
synchroniazed(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
public void doSomething(){
System.out.println("doSomething");
}
}
優點:既能很大程度上確保線程安全,又能實現延遲加載。
缺點:使用 volatile 關鍵字會使JVM對該段代碼的優化喪失,影響性能。并且在一些高并發的情況下,仍然可能創建多個實例,這稱為 雙重檢查鎖定失效 ,有一些書中作者均認為這是一種“丑陋”的單例實現方案。
靜態內部類實現單例
public class Singleton{
priavte Singleton{
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
//靜態內部類確保了在首次調用getInstance()的時候才會初始化SingletonHolder,從而導致實例被創建。
//并且由JVM保證了線程的安全。
priavte static class SingletonHolder{
priavte static final Singleton instance = new Singleton();
}
public void doSomething(){
System.out.println("doSomething");
}
}
這是單例模式最好的實現方法之一。
枚舉類實現單例
枚舉能夠確保實例的唯一性,能夠最大程度上確保線程安全,并且提供無償序列化機制。所以在不對延遲加載有太高要求的情況下,使用枚舉創建單例是最佳的方案!
public enum Singleton{
INSTANCE;
public void doSomething(){
System.out.println("doSomething");
}
}
拓展
以下幾種情況下JVM會自動幫助我們完成同步:
- 靜態初始化器(static{}代碼塊)初始化數據時;
- 訪問final字段時;
- 在創建線程之前創建對象;
- 線程可以看見它將要創建的對象時。
來自:http://www.jianshu.com/p/beddc8c8bbca
本文由用戶 zthenry 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!