前幾天看到一個問題议慰,問單例模式有幾種實現(xiàn)方式速址,由此今天來說一下這個問題龄糊。
首先說說什么是單例模式逆粹,它是指JAVA類有且只有一個全局訪問點,保證該類只創(chuàng)建一個實例炫惩。單例模式有三個基本的要點:
1僻弹、該類只有一個實例。
2他嚷、這個實例必須是自行創(chuàng)建蹋绽。
3、必須自行為整個系統(tǒng)提供該實例的訪問筋蓖。
一般情況下卸耘,單例模式分區(qū)餓漢模式和懶漢模式;餓漢模式會在類初始化的時候就開辟一塊內(nèi)存空間粘咖,它保證了多線程下實例的唯一性蚣抗,并且效率非常高;但是缺點在于當(dāng)類的成員比較多或者變量比較大的時候就開辟了空間對資源的消耗就比較大瓮下。為了避免餓漢模式的這個問題翰铡,懶漢模式會在系統(tǒng)使用到這個類是才去創(chuàng)建這個對象。
常規(guī)實現(xiàn)
餓漢模式:
public final class Singleton {
/**
* 自行創(chuàng)建私有實例
*/
private static Singleton instance = new Singleton();
/**
* 私有構(gòu)造
*/
private Singleton() {
}
/**
* 提供公有的全局訪問點
* @return
*/
public static Singleton getInstance() {
return instance;
}
}
懶漢模式:
public final class Singleton {
/**
* 自行創(chuàng)建私有實例
*/
private static Singleton instance = null;
/**
* 私有構(gòu)造
*/
private Singleton() {
}
/**
* 提供公有的全局訪問點
* @return
*/
public static Singleton getInstance() {
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
以上代碼是餓漢模式和懶漢模式的常用實現(xiàn)代碼讽坏;但是以上懶漢模式是存在問題的锭魔,試想,在多線程的場景下運行震缭,同時執(zhí)行到這段代碼時赂毯,可能會實例化多個實例战虏。
這時候拣宰,我們可以添加synchronized來同步鎖修飾getInstance方法党涕,保證其只被實例化一次
public static synchronized Singleton getInstance() {
if(instance==null){
instance = new Singleton();
}
return instance;
}
但是,這樣做了之后還是有問題的巡社,使用同步鎖會帶來鎖競爭膛堤,增加系統(tǒng)的性能開銷,這時候可以考慮將synchronized添加在if條件中:
public static Singleton getInstance() {
if(instance==null){
synchronized(Singleton.class){
instance = new Singleton();
}
}
return instance;
}
這樣晌该,感覺是完美了肥荔;但如果兩個線程同時進(jìn)入了if條件里面,雖然有同步鎖朝群,但是進(jìn)入到條件里面的線程依然會依次獲取鎖創(chuàng)建對象燕耿,然后再釋放同步鎖,所以在if條件里面還需要添加if條件的判斷
public static Singleton getInstance() {
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
這樣姜胖,通過synchronized+兩次if就實現(xiàn)了一個穩(wěn)定的單例模式結(jié)構(gòu)誉帅,但是此種結(jié)構(gòu)比較復(fù)雜,有沒有簡化一點的呢右莱?
內(nèi)部類實現(xiàn)
在餓漢模式中蚜锨,使用static修飾的instance,所以類在初始化完成就已經(jīng)被收集到類構(gòu)造器中了慢蜓。在多線程情況下亚再,JVM會保證只有一個線程能執(zhí)行這個方法,其他線程都會被阻塞等待晨抡。這種方式保證了內(nèi)存的可見性氛悬、順序性、原子性耘柱。
如果通過一個內(nèi)部類來完成成員的實例化圆雁,則也可以避免多線程重復(fù)創(chuàng)建實例的情況:
public final class Singleton {
/**
* 私有構(gòu)造
*/
private Singleton() {
}
/**
* 提供公有的全局訪問點
* @return
*/
public static Singleton getInstance() {
return InsideSingleton.instance;
}
/**
* 實例
*/
public static class InsideSingleton {
private static Singleton instance = new Singleton();
}
}
枚舉實現(xiàn)
相較于餓漢模式和懶漢模式,枚舉是實現(xiàn)方式就會更加簡潔
public final class Singleton {
/**
* 私有構(gòu)造
*/
private Singleton() {
}
/**
* 提供公有的全局訪問點
* @return
*/
public static Singleton getInstance() {
return SinletonEnum.SINLETON.getInstance();
}
/**
* 定義單例枚舉
*/
private enum SinletonEnum {
SINLETON;
private Singleton singleton;
SinletonEnum() {
singleton = new Singleton();
}
/**
* 構(gòu)建實例
*/
public Singleton getInstance() {
return singleton;
}
}
}