Android架構進階(四)—— 單例模式

單例模式

單例模式定義:在同一進程內,單例對象的類只允許存在一個實例。

  • 單例模式通常要求僅存在一個實例,那就是說無論什麼情況下都要求,我們只能創建一次實例。
  • 當我們未創建實例時,有多個線程同時調用實例創建方法,有時候會導致兩個實例同時被創造出來。
  • 所以爲了解決線程安全問題,我們通常爲指示類是否實例化的變量提供一個互斥鎖

1、單例模式實現方式

單例模式需要類包含提供外部使用的同一個實例對象,和一個獲取該對象的方法

通常獲得實例的方法都是靜態方法且命名爲getInstance()方法。

  • 將該類的構造方法定義爲私有方法,這樣其他類就無法通過new關鍵字去構造這個對象,只能通過getInstance()方法獲取該對象
  • 在該類內提供的getInstance()靜態方法中,我們判斷如果累持有的提供給外部的實例對象爲空時我們就創建一個實例並賦予該實例對象引用,如過不爲空時,我們就返回這個實例對象。

2、餓漢式【使用靜態常量】

public class A{
	private final static A instance = new A();
	private A(){}
	public static A getInstance(){
		return instance;
	}
}
優點 實現簡單粗暴,在類裝載時就實例化好了內部實例對象,可以避免線程同步問題
缺點 在類裝載時就實例化了內部實例,如果從未使用這個實例,對象依舊存在,浪費內存資源

3、餓漢式【使用靜態代碼塊】

public class A{
	private static A instance;
	static {
		instance = new A();
	}
	private A(){}
	
	public static A getInstance(){
		return instance;
	}
}

除了實現方式不同,與上一種方法基本類似。

優點 實現簡單粗暴,在類裝載時就實例化好了內部實例對象,可以避免線程同步問題
缺點 在類裝載時就實例化了內部實例,如果從未使用這個實例,對象依舊存在,浪費內存資源

4、懶漢式【效率低不推薦使用】

public class A{
	private static A instance;
	private A(){}

	public static A getInstance(){
		if(instance != null){
			instance = new A();
		}
		return instance;
	}
}

這樣我們就實現了懶加載,但是只能在單線程模式下工作,如果多線程同時調用getInstace()方法就會容易產生多個實例,所以切記最好不用這種方式。

那麼我們如何解決多線程下調用創建方法會創建多個實例這種問題呢,這裏我們引入synchronized關鍵字,將getInstance()方法鎖住只允許多個線程按順序排隊調用getInstance(),這樣就不會出現同時判斷instance != null 這種情況,避免了創建多個實例

public class A{
	private static A instance;
	private A(){}

	public static synchronized A getInstance(){
		if(instance != null){
			instance = new A();
		}
		return instance;
	}
}
優點 實現了懶加載,即不會再類裝載的時候就創建內部單例實例,只有當第一次調用getInstance()方法時纔會創建實例,避免了佔用額外內存資源
缺點 效率過低。每個線程在想獲得這個類的實例的時候,都必須進行同步,然而我們其實只是想要在實例化時限制同步,後面獲取實例時直接return當前實例就OK

5、懶漢式【Double-Check,推薦使用】

由於上種方式效率過低,所以我們進行改進

public class A{
	private static A instance;
	private A(){}

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

這樣雖然效率提升了,但是還是會出現最開始時的多次創建實例的情況,那麼,我們就通過Double-Check進行進一步改進

public class A{
	private static A instance;
	private A(){}

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

經過這樣改進後,除了代碼多了一點意外,基本上屬於比較完美的實現方式了,線程安全,延遲加載,效率較高。

6、靜態內部類【推薦使用】

這裏我們利用了JVM的特性,類的靜態屬性只會在第一次加載類的時候初始化,利用JVM這個特性保證了線程的安全性,因爲在類進行初始化時,別的線程是無法進入的。

public class A{
	private A(){}
	private static class AInstance{
		private static final A INSTANCE = new A();
	} 
	public static A getInstance(){
		return AInstance.INSTANCE;
	}
}

這種方式是java特有的實現方式,利用了JVM的特性,即滿足了延遲加載——只有當第一次調用getInstance()方法時,纔會裝載內部類AInstance,避免了線程不安全,延遲加載,效率高。

單例模式的優缺點

優點 在系統內只存在該類的一個對象,節省了系統資源,可以提高性能
缺點 不常用的實例使用單例模式時會導致該對象一直存在,需要手動釋放

適用場合

  • 需要頻繁創建和銷燬的對象;
  • 創建對象的過程中耗時過多或資源耗費過多,且常用的對象;
  • 工具類的對象通常可以使用這種方法
  • 頻繁訪問的數據庫或文件對象
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章