設計模式:單例模式(關於餓漢式和懶漢式)

設計模式:單例模式(關於餓漢式和懶漢式)

定義

  • 單例模式是一種常見的設計模式,目的是保證一個類中只能有一個實例,而且自行實例化並向整個系統提供這個實例,避免頻繁創建對象,節約內存

優缺點

優點

  • 單例類只有一個實例,節省了內存資源,對於一些需要頻繁創建銷燬的對象,使用單例模式可以提高系統性能;
  • 單例模式可以在系統設置全局的訪問點,優化和共享數據,例如前面說的Web應用的頁面計數器就可以用單例模式實現計數值的保存。

缺點

  • 單例模式一般沒有接口,擴展的話除了修改代碼基本上沒有其他途徑。

類加載順序

  • 先執行父類的靜態代碼塊和靜態變量初始化,靜態代碼塊和靜態變量的執行順序跟代碼中出現的順序有關。
  • 執行子類的靜態代碼塊和靜態變量初始化。
  • 執行父類的實例變量初始化
  • 執行父類的構造函數
  • 執行子類的實例變量初始化
  • 執行子類的構造函數

同時類加載的過程是線程私有的,別的線程無法進入

如果類已經被加載:靜態代碼塊和靜態變量不在執行,再創建類對象時,只執行與實例相關的變量初始化和構造方法

Static關鍵字

一個類中如果有成員變量或者方法被static關鍵字修飾,那麼該成員變量或方法將獨立於該類的任何對象。它不依賴類特定的實例,被類的所有實例共享,只要這個類被加載,該成員變量或方法就可以通過類名去進行訪問,它的作用用一句話來描述就是,不用創建對象就可以調用方法或者變量。

下面將列舉幾種單例模式的實現方式,其關鍵方法都是用static修飾的,並且,爲了避免單例的類被頻繁創建對象,我們可以用private的構造函數來確保單例類無法被外部實例化。

懶漢和餓漢模式

單例模式一般分爲兩種一種是餓漢式一種是懶漢式

餓漢式:在類加載時就完成了初始化,所以類加載比較慢,單獲取對象的速度比較快

懶漢式:在類加載時不初始化,等到第一次被使用時才初始化

Java代碼實現

餓漢式(可用)

public class Singleton {

    private final static Singleton INSTANCE = new Singleton();
    
    private Singleton(){}

    public static Singleton getInstance(){
        return INSTANCE;
    }

}
  • 在類加載的時候就完成了實例化,避免了多線程的同步問題,但是因爲在類加載就實例化了,沒有達到懶加載的效果,如果該實例沒有被使用,內存就浪費了

懶漢式

普通的懶漢式
public class Singleton {

    private static Singleton instance = null;

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

只有在方法第一次被訪問時纔會實例化,達到了懶加載的效果,對**getInstance()加了鎖的處理,保證了同一時刻只能有一個線程訪問並獲得實例,**但是因爲synchronized是修飾整個方法,每個線程訪問都要進行同步,但是這個方法只執行一次實例化代碼就夠了,每次同步導致效率低下

雙重檢查懶漢式(推薦,可用)
public class Singleton {
	// volatile 讓變量每次在使用的時候,都從主存中取。而不是從各個線程的“工作內存”
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

這種寫法使用了兩個if判斷,並且同步的不是方法,而是代碼塊,效率較高。防止多次初始化對象

靜態內部類

public class Singleton {

    private Singleton() {}

    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }

}
  • 這種靜態內部類方式在Singleton類被裝載時不會立即實例化,而是在需要實例化時,調用getInstance方法,纔會裝載Single通Instance類,從而完成對象的實例化
  • 因爲類的靜態屬性只會在第一次加載類的時候初始化,也就保證了SingletonInstance中的對象只會被實例化一次,並且這個過程也是線程安全的

枚舉

public enum Singleton {
    INSTANCE;
}
  • 線程安全:因爲Java虛擬機在加載枚舉類的時候會使用ClassLoader的方法,這個方法使用了同步代碼塊來保證線程安全。
  • 避免反序列化破壞對象,因爲枚舉的反序列化並不通過反射實現。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章