1.單例模式介紹
單例模式是一種對(duì)象創(chuàng)建模式但校,它用于產(chǎn)生一個(gè)對(duì)象的具體實(shí)例夕吻,它可以確保系統(tǒng)中一個(gè)類只產(chǎn)生一個(gè)實(shí)例线衫。java中的單例模式的討論范圍是在JVM凿可。
單例模式的好處,①對(duì)于頻繁使用的對(duì)象授账,可以省略創(chuàng)建對(duì)象所花費(fèi)的時(shí)間枯跑,這對(duì)于那些重量級(jí)對(duì)象而言,是非嘲兹龋可觀的一筆系統(tǒng)開銷敛助。②由于new操作的次數(shù)減少,因而對(duì)系統(tǒng)內(nèi)存的使用頻率也會(huì)降低屋确,這將減輕GC壓力纳击,所短GC停頓時(shí)間。
2.單例的多種寫法和各自特點(diǎn)
1.餓漢模式
public class HungurySingleton { private static final HungurySingleton mInstance = new HungurySingleton(); private HungurySingleton(){ System.out.println("create"); } public static HungurySingleton getInstance(){ return mInstance; } public static void printHello(){ System.out.println("Hello"); } public static void main(String[] args) { HungurySingleton.printHello(); } }
上面的代碼就是餓漢模式的單例攻臀,這種寫法的特點(diǎn)就是焕数,當(dāng)HungurySingleton這個(gè)類被JVM加載時(shí),單例對(duì)象就會(huì)被創(chuàng)建刨啸,正因?yàn)檫@樣堡赔,這種寫法就有一個(gè)先天不足,無法對(duì)instance實(shí)例做延時(shí)加載设联,當(dāng)執(zhí)行上面代碼時(shí)會(huì)發(fā)現(xiàn)善已,明明只是調(diào)用了HungurySingleton 的一個(gè)類方法,但是HungurySingleton實(shí)例還是被創(chuàng)建了离例。
2.懶漢模式
public class LazySingleton { private static LazySingleton mInstance; private LazySingleton(){} public static LazySingleton getInstance(){ if (mInstance == null) mInstance = new LazySingleton(); return mInstance; } }
上面代碼就是單例的懶漢模式换团,它有致命缺陷,當(dāng)在多線程環(huán)境下粘招,它根本無法保證單例啥寇,所以我們又有了線程安全的懶漢模式。
public class LazySingleton { private static LazySingleton mInstance; private LazySingleton(){} public static synchronized LazySingleton getInstance(){ if (mInstance == null) mInstance = new LazySingleton(); return mInstance; } public static LazySingleton getInstance2(){ synchronized (LazySingleton.class){ if (mInstance == null) mInstance = new LazySingleton(); return mInstance; } } }
主要是使用了synchronized關(guān)鍵字洒扎,可以用它來修飾方法辑甜,也可以使用代碼塊,但是它同樣有缺陷袍冷,就是性能效率低磷醋。
3.DCL(雙檢查鎖機(jī)制)
public class DclSingleton { private static volatile DclSingleton mInstance; private DclSingleton(){} public static DclSingleton getInstance(){ if (mInstance == null){ synchronized (DclSingleton.class){ if (mInstance == null) mInstance = new DclSingleton(); } } return mInstance; } }
DCL單例模式是對(duì)懶漢模式的一種優(yōu)化,代碼中實(shí)例被volatile所修飾胡诗,這是因?yàn)槿绻皇褂胿olatile關(guān)鍵字邓线,JVM的即時(shí)編譯器中指令重排序優(yōu)化可能會(huì)影響代碼執(zhí)行淌友,從而使代碼出錯(cuò),達(dá)不到單例效果骇陈,volatile正是解決方法震庭。關(guān)于volatile的使用以后再討論。
4.靜態(tài)內(nèi)部類寫法
public class StaticInnerSingleton { private StaticInnerSingleton(){} public static StaticInnerSingleton getInstance(){ return SingletonHolder.mInstance; } private static class SingletonHolder{ private static final StaticInnerSingleton mInstance = new StaticInnerSingleton(); } }
靜態(tài)內(nèi)部類寫法是對(duì)DCL寫法的更近一步優(yōu)化你雌,建議實(shí)際項(xiàng)目中使用器联,它寫法簡(jiǎn)單明了,線程安全婿崭,效率高拨拓,同時(shí)還能滿足延遲加載。這種寫法主要使用了JVM的同步控制氓栈,什么是同步控制渣磷,就是兩個(gè)關(guān)鍵字的使用,一個(gè)是static授瘦,一個(gè)是final醋界。static保證唯一性,final保證不可改變性奥务,這樣就能保證在JVM中這個(gè)實(shí)例時(shí)唯一且不可改變的物独,同時(shí)還是線程安全的袜硫,沒有使用synchronized關(guān)鍵字氯葬,效率還高。
5.枚舉寫法
public enum EnumSingleton { INSTANCE; public void doSomething(){} }
這就是枚舉單例的寫法婉陷,真是極簡(jiǎn)了帚称,由于枚舉是java1.5以后才有的,所以這種寫法要在1.5以后的版本中才能使用秽澳。
簡(jiǎn)簡(jiǎn)單單的一點(diǎn)代碼就實(shí)現(xiàn)了一個(gè)線程安全闯睹,lazy loading的單例,與其說是寫法鬼斧神工担神,不如說是恰如其分地應(yīng)用了enum的三個(gè)特性楼吃,自由序列化,線程安全妄讯,保證單例孩锡。
6.總結(jié)
餓漢模式:無法對(duì)instance實(shí)例進(jìn)行延遲加載
懶漢模式:多線程下無法保證唯一性
線程安全的懶漢模式:使用synchronized影響性能
DCL模式:被JVM的即時(shí)編譯器的指令重排序因素影響,可使用volatile解決
靜態(tài)內(nèi)部類/枚舉模式:延遲加載/線程安全/效率保證
所以亥贸,推薦在項(xiàng)目中使用靜態(tài)內(nèi)部類和枚舉去實(shí)現(xiàn)單例躬窜。