Android重拾設計模式系列——單例模式的5種實現

zthenry 7年前發布 | 15K 次閱讀 單例模式 Android開發 移動開發

封面-設計模式.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");
    }
}

解釋:

  1. 餓漢式是利用了 static 關鍵字在類加載時就會進行初始化,并且緩存到 靜態內存 中的特點,確保了調用 getInstance() 時,無須擔心 instance 為null;
  2. 通過 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");
    }
}

解釋:

  1. 懶漢式體現了延遲加載的的思想。對象實例只有在第一次調用 getInstance() 方法時才會被創建,一定程度上的節約了系統資源;
  2. 懶漢式在單線程下能夠很好的工作,但是并發下就很有可能會創建多個實例。

優點:能夠實現延遲加載,節約內存。在單線程中能很好工作。

缺點:并發下可能會創建多個實例,每次判斷都會耗費一些時間。

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 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!