Android重拾設(shè)計模式系列——單例模式的5種實現(xiàn)

個人博客CoorChice,https://chenbingx.github.io/ ,最新文章將會首發(fā)CoorChice的博客,歡迎探索哦 !
同時,搜索微信公眾號CoorChice,或掃描文章末尾二維碼,可以關(guān)注我的微信公眾號。同期文章也將會優(yōu)先推送到微信公眾號中,以提醒您有新鮮文章出爐。

封面-設(shè)計模式.png

單例模式是我們最常使用,也是最簡單的一種模式,主要用于只想系統(tǒng)中存在一個實例的情況,比如某個Manager。

定義及實質(zhì)

  • 定義
    確保某一個類只有一個實例,而且自行實例化并向系統(tǒng)提供這個實例。

  • 實質(zhì)
    控制實例數(shù)量,確保只有一個實例。

模式圖解

單例模式UML圖

單例模式UML圖

很直觀明了,很簡單。下面來看看單例模式的不同實現(xiàn)方案。

餓漢式

public class Singleton{
    private static fianl Singleton instance = new Singleton();
    
    //私有化構(gòu)造器,避免外部訪問。使用反射仍然可以訪問,所以安全是相對的。
    //但仍然可以通過哈希值等進(jìn)行限制,提高安全性。
    priavte Singleton(){
    }
    
    //對外暴露的接口,用于獲取實例
    public static Singleton getInstance(){
        return instance;
    }
    
    public void doSomething(){
        System.out.println("doSomething");
    }
}

解釋:

  1. 餓漢式是利用了static 關(guān)鍵字在類加載時就會進(jìn)行初始化,并且緩存到靜態(tài)內(nèi)存 中的特點,確保了調(diào)用getInstance() 時,無須擔(dān)心instance 為null;
  2. 通過fianl 關(guān)鍵字,式單例在多線程情況下的安全,因為JVM會自動對fianl 進(jìn)行上鎖同步。

優(yōu)點: 能夠在線程安全的情況下實現(xiàn)單例。
缺點: 由于類一加載就會創(chuàng)建實例,所以會較早占用系統(tǒng)資源。

懶漢式

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. 懶漢式體現(xiàn)了延遲加載的的思想。對象實例只有在第一次調(diào)用getInstance() 方法時才會被創(chuàng)建,一定程度上的節(jié)約了系統(tǒng)資源;
  2. 懶漢式在單線程下能夠很好的工作,但是并發(fā)下就很有可能會創(chuàng)建多個實例。

優(yōu)點: 能夠?qū)崿F(xiàn)延遲加載,節(jié)約內(nèi)存。在單線程中能很好工作。
缺點: 并發(fā)下可能會創(chuàng)建多個實例,每次判斷都會耗費一些時間。

DCL雙重檢查實現(xiàn)單例

public class Singleton{
    //這里使用了volatile關(guān)鍵字,它能夠確保insatnce變量每次都直接從主內(nèi)存(而不是寄存器)中加載最新賦值。
    private volatile static Singleton instance = null;

    priavte Singleton(){
    }
    
    //這里進(jìn)行了兩次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");
    }
}

優(yōu)點: 既能很大程度上確保線程安全,又能實現(xiàn)延遲加載。
缺點: 使用volatile 關(guān)鍵字會使JVM對該段代碼的優(yōu)化喪失,影響性能。并且在一些高并發(fā)的情況下,仍然可能創(chuàng)建多個實例,這稱為雙重檢查鎖定失效 ,有一些書中作者均認(rèn)為這是一種“丑陋”的單例實現(xiàn)方案。

靜態(tài)內(nèi)部類實現(xiàn)單例

public class Singleton{
    priavte Singleton(){
    }
    
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
    
    //靜態(tài)內(nèi)部類確保了在首次調(diào)用getInstance()的時候才會初始化SingletonHolder,從而導(dǎo)致實例被創(chuàng)建。
    //并且由JVM保證了線程的安全。
    priavte static class SingletonHolder{
        priavte static final Singleton instance = new Singleton();
    }
    
    public void doSomething(){
        System.out.println("doSomething");
    }
}

這是單例模式最好的實現(xiàn)方法之一。

枚舉類實現(xiàn)單例

枚舉能夠確保實例的唯一性,能夠最大程度上確保線程安全,并且提供無償序列化機制。所以在不對延遲加載有太高要求的情況下,使用枚舉創(chuàng)建單例是最佳的方案!

public enum Singleton{
    INSTANCE;
    
    public void doSomething(){
        System.out.println("doSomething");
    }
}

拓展

以下幾種情況下JVM會自動幫助我們完成同步:

  • 靜態(tài)初始化器(static{}代碼塊)初始化數(shù)據(jù)時;
  • 訪問final字段時;
  • 在創(chuàng)建線程之前創(chuàng)建對象;
  • 線程可以看見它將要創(chuàng)建的對象時。
CoorChice的公眾號
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容