最近突然想回顧一下設計模式,很多東西是要回過頭來總結一下的。今天先回顧一下單例吧。
很多時候覺得挺搞笑的,去面試的時候如果人家問你設計模式,一般都是要你寫個單例模式。去年來北京好幾家面試都是問我這個。當時我就想這個能反映出一個人的水平來嗎?還是說更多的是反映出這個公司的水平呢?
隨著一年的應用,很多地方都用過之后覺得,單例這個東西雖然簡單,可是現實是復雜的。所以單例這個簡單的模式也不能太小瞧咯。
單例其實有很多種實現,這是其中的一種,延遲加載的(好像英文叫Lazy?):
[下面代碼中所有的構造器都是私有的,這里我就省略不寫了。]
public class ClassName {
public static ClassName getInstance(){
if(instance == null)
{
instance = new ClassName();
}
return instance;
}
private static ClassName instance;
}
這種的好處是我們的單例使用時才進行初始化,這樣方便我們在系統啟動時做些小動作。但是這個方式不是線程安全的,想要完成一個線程安全的單例,有幾種方式:
(一)
public class ClassName {
public static ClassName getInstance(){
return instance;
}
private static ClassName instance = new ClassName();
}
這種方式,可以保證我們的單例是線程安全的,畢竟我們唯一的實例在系統初始化的時候就構造了。可是Java的機制是static級別的變量初始化時互相調用是會報異常的。所以隨著系統的擴展,尤其還會有一些新手或者粗心大意的家伙(比如說,我)會亂用你的方法。一不小心就造成問題了。而且,你也失去了第一個方式中的一個小優勢,不能在系統啟動時做點小動作了。
(二)
public class ClassName {
public static synchronized ClassName getInstance(){
if(instance == null)
{
instance = new ClassName();
}
return instance;
}
private static ClassName instance;
}
這樣倒是線程安全了,也可以延遲加載,但是從今以后這個getInstance方法就是
synchronized的了,那絕對是很影響效率的。我跟朋友討論提出了幾種寫法,以期既能使單例可以在系統啟動不至于數據已經煮成熟飯又是線程安全的:(少數人討論結果,代碼可能會比較丑陋,僅供參考,歡迎拍磚)
public class ClassName {
public static ClassName getInstance(){
if(instance == null)
{
instance = ClassName.createInstance();
}
return instance;
}
private static synchronized ClassName createInstance(){
if(instance == null)
{
return new ClassName();
}else{
return instance;
}
}
private static ClassName instance;
}
這種寫法就很好的解決了這些問題。
還有一種寫法是這樣的,這個不是延遲加載的。而是采用了一種取巧的方式。
public class ClassName {
public static ClassName getInstance(){
if(!instance.isInit)
{
instance.initSingleton();
}
return instance;
}
private synchronized void initSingleton() {
if(!isInit)
{
reset();//這名字是有點怪異,我沒時間想太好聽的名字
isInit = true;
}
}
public void reset(){
//.....真正進行數據初始化的地方
}
private boolean isInit = false;
private static ClassName instance = new ClassName();
}
將所有的初始化代碼搬到構造器之外。這是專為數據初始化和復位進行的設計。所以我把reset開放了出來。