關於單例模式的理解

最近面了好多家公司,幾乎都會問到有關單例模式的問題,當然基礎的單例模式大家可能都寫得出來,但面試官深入問下去呢?很可能就矇了,所以在這裡就整理幾個單例模式防止自己忘記。

單例模式是做什麼的?

根據維基百科的解釋:

單例模式,也叫單子模式,是一種常用的軟件設計模式。 在應用這個模式時,單例對象的類必須保證只有一個實例存在。 許多時候整個系統只需要擁有一個的全局對象,這樣有利於我們協調系統整體的行為。
白話一點說,就是保證一個對象(物件)中,只有一個實例的存在。


懶漢式(線程不安全):

下面這個單例模式是最基礎的單例模式,只有需要加載這個實例的時候才會進行加載。

package Interview.Singleton.LazyInitialization;

public class SingletonBasic {
	private static SingletonBasic instance = null;
	
	private SingletonBasic() {
		
	}
	
	public static SingletonBasic getInstange() {
		if(instance == null)
			instance = new SingletonBasic();
		return instance;
	}
}

這種單例模式雖然簡單,但也存在致命的問題,就是在多線程環境時,多個線程調用getInstance()時,並無法保證只有一個實例。在面試的時候問到了單例模式,寫出上面這種模式,面試官肯定會繼續追問下去(當然也可以寫出來拖延時間XD~~~)。


懶漢式(線程安全)

既然最基礎的懶漢式無法滿足要求,那只要將getInstance()設為同步,就可以保證只有一個實例。

package Interview.Singleton.LazyInitialization;

public class SingletonSynchronized {
	private static SingletonSynchronized instance = null;
	
	private SingletonSynchronized() {
		
	}
	
	public static synchronized SingletonSynchronized getInstange() {
		if(instance == null)
			instance = new SingletonSynchronized();
		return instance;
	}
}

雖然上面這種方法保證了線程安全,但它的效率不高(synchronized效率本來就不怎樣),故我們又引入了下面另外一種方法。


懶漢式(雙重檢驗鎖)

這種方法使用雙重檢驗,第一次檢驗是否實例存在,第二次是在同步中再一次檢查是否有實例存在,第二次檢查是為了防止多線程同時調用生成多個實例。

package Interview.Singleton.LazyInitialization;

public class SingletonDoubleCheckLocking {
	private static volatile SingletonDoubleCheckLocking instance = null;
	private SingletonDoubleCheckLocking() {
		
	}
	
	public static SingletonDoubleCheckLocking getInstance() {
		if(instance == null)
			synchronized (SingletonDoubleCheckLocking.class) {
				if(instance == null) {
					instance = new SingletonDoubleCheckLocking();
				}
			}
					
		return instance;
	}
	
}
為什麼instance要聲明為volatile(保證instance這個不會指到未分配的內存空間)呢?因為在新增instance這個實例的時候,Java虛擬機會做下面三件事情:

  1. 為instance分配內存空間
  2. 調用SingletonDoubleCheckLocking的構造函數(建構子)
  3. 將instance的指針指向1所分配的空間
但Java虛擬機會進行指令重排序的優化,所以不能保證是1 2 3順序執行,可能是1 3 2或是其他執行順序,而多個線程在運行時可能會在instance正在初始化的時候就搶佔了這個instance,導致instance沒有建構完成就返回這個實例,想當然就會返回一個null,然後就跳出錯誤啦@@

但這樣寫還滿複雜的,有沒有更簡單一點的方法呢?


餓漢式

使用餓漢式就不用關注這麼多情況了,因為單例的實例已經聲明為final以及static,故在加載這個類的時候,實例就會被初始化,所以實例本身就是線程安全的。

package Interview.Singleton.HurryInitialization;

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

但這種寫法當然還是有缺點的,例如:

  1. 有時候不需要實例化該實例的時候,仍然會初始化實例,浪費內存空間
  2. 或是創建實例的時候需要依賴於參數或是配置文件的時候,並沒辦法指定參數給個實例


尚未完成 待續....


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值