幾種常見的單例模式的寫法及優缺點比較
概述
今天再來總結一篇開發中常用的設計模式單例模式,其定義是單例對象的類只能允許一個實例存在,它雖然有多種常見的寫法,但是其基本思想不變:
- 構造器私有化,使外部無法無法new 對象
- 提供一個獲取對象的方法 即提供一個get方法獲取第三部中的屬性,但是要求所有對象共用故而用static修飾
- 內部提供一個實例 作爲屬性 要求私有化 靜態化
private static
單例模式幾種常見的寫法
1:餓漢模式
- 優點:寫法比較簡單、線程安全,在類加載的時候就完成實例化。
- 缺點:在類加載的時候就完成實例化,沒有達到Lazy Loading(懶加載)的效果。如果從始至終從未使用過這個實例,則會造成內存的浪費。
public class SingleInstance {
//餓漢模式線程安全
//step1 :私有化構造器
private SingleInstance() {
}
//step2 :提供私有化靜態屬性
private static SingleInstance sSingleInstance = new SingleInstance();
//step3 : 通過獲取該屬性的方法
public static SingleInstance getInstance() {
return sSingleInstance;
}
}
2:懶漢模式
- 優點:這種方式實現了Lazy Loading的效果,單線程提可保證單例
- 缺點:線程不安全,只適用於單線程不適用於多線程
public class SingleInstance {
//懶漢模式
//step1 :私有化構造器
private SingleInstance(){}
//step2 :提供私有化靜態屬性
private static SingleInstance sInstance;
//step3 : 通過獲取該屬性的方法
public static SingleInstance getInstance(){
if (sInstance == null) {
sInstance=new SingleInstance();
}
return sInstance;
}
}
3:懶漢模式:同步方法
通過synchronized
修飾該方法,保證線程安全
- 優點:實現了懶加載,並且線程安全
- 缺點:效率低不推薦使用,因爲使用了
synchronized
修飾方法,每次使用時都得同步
public class SingleInstance {
//懶漢模式 同步方法
//step1 :私有化構造器
private SingleInstance(){}
//step2 :提供私有化靜態屬性
private static SingleInstance sInstance;
//step3 : 通過獲取該屬性的方法 synchronized修飾該方法
public static synchronized SingleInstance getInstance(){
if (sInstance == null) {
sInstance=new SingleInstance();
}
return sInstance;
}
}
4:懶漢模式:同步代碼塊
通過synchronized
修飾該方法,保證線程安全
- 優點:實現了懶加載,
- 缺點:線程不安全,和方法二一樣不適用於多線程,不推薦使用
public class SingleInstance {
//懶漢模式 同步代碼快
//step1 :私有化構造器
private SingleInstance(){}
//step2 :提供私有化靜態屬性
private static SingleInstance sInstance;
//step3 : 通過獲取該屬性的方法 synchronized修飾該方法
public static SingleInstance getInstance(){
if (sInstance == null) {
synchronized (SingleInstance.class){
sInstance=new SingleInstance();
}
}
return sInstance;
}
}
5:雙重鎖機制(推薦)
優點:實現了懶加載,線程安全,效率高,第一層判null提高了鎖機制的使用效率,第二層判null纔是真正意義的單例。
public class SingleInstance {
//雙重鎖
//step1 :私有化構造器
private SingleInstance(){}
//step2 :提供私有化靜態屬性
private static SingleInstance sInstance;
//step3 : 通過獲取該屬性的方法 synchronized修飾該方法
public static SingleInstance getInstance(){
if (sInstance == null) {//第一層:保證的多線程只有第一次調用getInstance 的時候纔會加鎖初始化
synchronized (SingleInstance.class){
if (sInstance==null) {// 第二層 實現了單例模式
sInstance=new SingleInstance();
}
}
}
return sInstance;
}
6:靜態內部類(推薦)
優點:避免了線程不安全,延遲加載,效率高。
public class SingleInstance {
//靜態內部類
//step1 :私有化構造器
private SingleInstance(){}
//step2 :提供私有化靜態屬性
private static SingleInstance sInstance;
//step3 : 通過獲取該屬性的方法 synchronized修飾該方法
public static SingleInstance getInstance(){
//只有調用此方法的時候纔會初始化單例,實現了懶加載,因爲類(Single)的加載只有一次,從而保證了單例
return Single.sInstance;
}
private static class Single{
private static SingleInstance sInstance=new SingleInstance();
}
}
這種方式跟餓漢式方式採用的機制類似,但又有不同。兩者都是採用了類裝載的機制來保證初始化實例時只有一個線程。不同的地方在餓漢式方式是隻要Singleton類被裝載就會實例化,沒有Lazy-Loading的作用,而靜態內部類方式在Singleton類被裝載時並不會立即實例化,而是在需要實例化時,調用getInstance方法,纔會裝載SingletonInstance類,從而完成Singleton的實例化。
類的靜態屬性只會在第一次加載類的時候初始化,所以在這裏,JVM幫助我們保證了線程的安全性,在類進行初始化時,別的線程是無法進入的。
單例模式的優缺點以及使用場景
優點: 系統內存中該類只存在一個對象,節省了系統資源,對於一些需要頻繁創建銷燬的對象,使用單例模式可以提高系統性能。
優點:
系統內存中該類只存在一個對象,節省了系統資源,對於一些需要頻繁創建銷燬的對象,使用單例模式可以提高系統性能。
缺點:
單例類的職責過重,在一定程度上違背了“單一職責原則”。
適用場景:
- 工具類對象;
- 需要頻繁的進行創建和銷燬的對象,並且創建對象時耗時過多或耗費資源過多,如:訪問I0和數據庫等資源或者有很多個地方都用到了這個實例。
- 頻繁訪問數據庫或文件的對象,