1.什么是單例模式
在現實生活中存在着有這樣的特點的一些類:
A.這些類只能有一個實例;
B.這些能夠自動實例化;
C.這個類對整個系統可見,即必須向整個系統提供這個實例。
不妨舉一個具體的單例模式的例子:比如教室里面的教師和學生都是需要在黑板上寫字的,但是一般的情況下,教室里面應該只有一個黑板吧,它是教師和學生公用滴。這時就要想辦法保證取得的黑板是一個共享的唯一的對象。而單例模式就是解決這類問題的一個已經成型的模式。
2.如何實現單例模式
單例模式的實現通常有兩種方式:“餓漢式”和“懶漢式”。
2.1餓漢式:java實現代碼如下:
Public class Singleton
{
Private static final Singleton singleton = new Singleton();
Private Singleton()
{
}
Public static Singleton getInstance()
{
Return singleton;
}
}
小結:采用懶漢式方法實現單例模式的方法是在類的內部實例化一個靜態變量singleton,這是比較好理解的,既然一個類要滿足上面的A,B,C三個特點,那么很自然的能想到聲明為一個類的靜態變量啦。還有一點要注意的哦,那就是該類的構造方法是私有的。這樣類就不提供默認的構造函數了,所以也就不可以實例化了。呵呵,看看懶漢式吧,對比一下。
2.2懶漢式:java實現代碼如下:
Public class Singleton
{
Private static final Singleton singleton;//未初始化
Private Singleton()
{
}
Public static Singleton getInstance()
{
If(singleton == null)
{
Singleton = new Singleton();
}
}
}
小結:發現這兩種方式的區別了嗎?懶漢式也是通過一個類的靜態變量實現的。但是並沒有直接初始化。而是在函數getInstance()中實例化的,也就是每次想用這個實例的時候初始化的,如果已經初始化了,那么就不用初始化了,這樣也很好啊,對的,其實懶漢式是比較常用的實現方式。
3.多線程下的單例模式
知道了什么是單例模式,也知道了單例模式怎么實現,是不是神功已經練成了呢?哈哈哈,真正地武林高手,不僅僅是自己的功夫深,還要知道自己的功夫有什么漏洞,讓我們看看上面的實現方式的bug吧。(看標題就明白了吧!!)
對,那就是在多線程的情況下,會有問題。對於懶漢式實現的方式,如果現在存在着線程A和B,代碼執行情況是這個樣子的,線程A執行到了If(singleton == null),線程B執行到了Singleton = new Singleton();線程B雖然實例化了一個Singleton,但是對於線程A來說判斷singleton還是木有初始化的,所以線程A還會對singleton進行初始化。看看,出現問題了吧,年輕人不要懼怕問題,看看怎么解決吧。這就要用到Synchronized(同步)了,java編程語言中提供了兩種同步方式:“同步方法”和“同步聲明”。
於是乎,只要把Public static Singleton getInstance()加上一個Synchronized就ok了,
Public static Synchronized Singleton getInstance()。這樣的話,當線程B訪問這個函數的時候,其他的任何要訪問該函數的代碼不能執行,直到線程B執行完該函數(這是利用鎖實現的)。看看很容易解決的,所以嘛,記住哦,出現問題不要怕啊。
這樣寫東西是真的有點煩人,但是我不得不說這樣還是不太好的,因為多個線程訪問同一個函數的時候,那么只能有一個線程能夠訪問這個函數,這顯然效率有點低吧,其實可以用另外一種不同方式。看看代碼:
Public static Singleton getInstance()
{
If(singleton == null)
{
Synchronized(singleton.class)
{
Singleton = new Singleton();
}
}
}
這種方式將在方法上的聲明轉移到了內部的代碼塊中,只有當singleton=null時,才需要鎖機制,但是如果線程A和B同時執行到了Synchronized(singleton.class),雖然也是只有一個線程能夠執行,假如線程B先執行,線程B獲得鎖,線程B執行完之后,線程A獲得鎖,但是此時沒有檢查singleton是否為空就直接執行了,所以還會出現兩個singleton實例的情況。於是雙重檢查模式(DCL)就出現了。代碼如下:
Public static Singleton getInstance()
{
If(singleton == null)
{
Synchronized(singleton.class)
{
If(singleton == null)
{
Singleton = new Singleton();
}
}
}
}
小結:注意哦,多線程在訪問全局變量時是比較煩人的啊,然中影響數據的一直性。