單例模式的6種寫法及不同(精華版)
1、懶漢模式。執行緒不安全,但可以延遲
【不推薦,不使用】
<span style="font-size:12px;">public class Singleton1 {
private static Singleton1 singleton1=null;
private Singleton1(){}
public static Singleton1 getIntance(){
return singleton1;
}
}</span>
註釋: 這種寫法lazy loading很明顯,但是致命的是在多執行緒不能正常工作。
2、懶漢模式。執行緒安全。但可以延遲。
使用情況:【不要求延遲時, 可以使用,不推薦】
<span style="font-size:12px;">class Singleton2{
private tatic Singleton2 singleton2=null;
private Singleton2(){}
public static synchronized Singleton2 getIntance(){
return singleton2;
}
}</span>
註釋: 這種寫法能夠在多執行緒中很好的工作,而且看起來它也具備很好的lazy loading,但是,遺憾的是,效率很低,99%情況下不需要同步
3、餓漢模式。 所有餓漢模式的單例都是執行緒安全的。
使用情況:【常用】
<span style="font-size:12px;">class Singleton3{
private static Singleton3 singleton3=new Singleton3();
private Singleton3(){}
public static Singleton3 getIntance(){
return singleton3;
}
}</span>
註釋: 這種方式基於classloder機制避免了多執行緒的同步問題, 不過,instance在類裝載時就例項化,雖然導致類裝載的原因有很多種,
在單例模式中大多數都是呼叫getInstance方法, 但是也不能確定有其他的方式(或者其他的靜態方法)導致類裝載,
這時候初始化instance顯然沒有達到lazy loading的效果
4、餓漢模式。靜態內部類
【推薦使用】
<span style="font-size:12px;">class Singleton4 {
private static class SingletonHolder {
private static final Singleton4 INSTANCE = new Singleton4();
}
private Singleton4 (){}
public static final Singleton4 getInstance() {
return SingletonHolder.INSTANCE;
}
}</span>
註釋:這種方式同樣利用了classloder的機制來保證初始化instance時只有一個執行緒,它跟第三種和第四種方式不同的是(很細微的差別):
第三種和第四種方式是隻要Singleton類被裝載了,那麼instance就會被例項化(沒有達到lazy loading效果),而這種方式是Singleton類被裝載了,
instance不一定被初始化。因為SingletonHolder類沒有被主動使用,只有顯示通過呼叫getInstance方法時,才會顯示裝載SingletonHolder類,從而例項化instance。
想象一下,如果例項化instance很消耗資源,我想讓他延遲載入,另外一方面,我不希望在Singleton類載入時就例項化,因為我不能確保Singleton類還可能在其他的地方被主動使用從而被載入,那麼這個時候例項化instance顯然是不合適的。這個時候,這種方式相比第三和第四種方式就顯得很合理。
5、雙重校驗鎖 。
使用情況:【 推薦使用,常用,用吧,非常不錯啦】
class Singleton5
{
private volatile static Singleton4 singleton;
private Singleton5 (){}
public static Singleton5 getSingleton() {
if (singleton == null) {
synchronized (Singleton5.class) {
if (singleton == null) {
singleton = new Singleton5();
}
}
}
return singleton5;
}
}
註釋:在雙重檢查鎖中,程式碼會檢查兩次單例類是否有已存在的例項,一次加鎖一次不加鎖,一次確保不會有多個例項被建立。宣告
_instance
變數時使用了volatile關鍵字。沒有volatile
修飾符,可能出現Java中的另一個執行緒看到個初始化了一半的_instance
的情況,但使用了volatile
變數後,就能保證先行發生關係(happens-before
relationship)。對於volatile
變數_instance
,所有的寫(write)都將先行發生於讀(read)
6、列舉單例模式。
【好處不外乎三點:1.執行緒安全 2.不會因為序列化而產生新例項 3.防止反射攻擊】
private Object readResolve(){
return INSTANCE;
}
註釋: 程式碼就這麼簡單,你可以使用EasySingleton.INSTANCE呼叫它,比起你在單例中呼叫getInstance()方法容易多了。
下面是我總結的使用列舉實現單例模式的幾個原因。(詳細內容,其他網站上詳細看,資源很多,我就不多說了)
列舉單例模式程式碼簡潔 這是迄今為止最大的優點,如果你曾經在java5之前寫過單例模式實現程式碼,那麼你會知道即使是使用雙檢鎖你有時候也會返回不止一個例項物件。雖然這種問 題通過改善java記憶體模型和使用volatile變數可以解決,但是這種方法對於很多初學者來說寫起來還是很棘手。相比用 synchronization的雙檢索實現方式來說,列舉單例就簡單多了。
用列舉實現的單例:這是我們通常寫列舉單例的方式,它可能包含例項變數和例項方法,但是簡單來說我什麼都沒用,需要注意的是如果你使用例項方法,你就需要確保方法的執行緒安全性,避免它會影響物件的狀態。通常情況下列舉裡面建立例項是執行緒安全的,但是其它的方法就需要程式設計者自己去考慮了。