23种设计模式之《单例模式》
什么是單例模式
單例模式是23種設(shè)計(jì)模式中最簡(jiǎn)單和易用的模式。在某些情境下,如在一個(gè)上市公司中,有很多不同級(jí)別的員工,但是公司的CEO或者CTO都是只有一個(gè)的,CEO或者CTO在公司里就要求是一個(gè)單例。單例模式,就是某個(gè)類因?qū)嶋H情況的需要,要求在全局的范圍內(nèi)只能有唯一的實(shí)例對(duì)象,這個(gè)對(duì)象是常駐內(nèi)存的,可以重復(fù)使用,降低重復(fù)創(chuàng)建對(duì)象的開銷。
單例模式的特點(diǎn)
- 類的構(gòu)造函數(shù)是私有的
- 在類內(nèi)部實(shí)例化對(duì)象,并通過(guò)靜態(tài)方法向外提供實(shí)例化的對(duì)象
下面主要講解實(shí)現(xiàn)單例模式的方法以及它們的優(yōu)缺點(diǎn)
單例模式的實(shí)現(xiàn)
單例模式的目的,就是要確保在全局范圍內(nèi)某個(gè)類的對(duì)象是唯一的。所以實(shí)現(xiàn)單例模式時(shí),我們至少要考慮兩個(gè)影響對(duì)象創(chuàng)建的因素。
- 在并發(fā)的環(huán)境下的線程安全
- 反序列化
餓漢實(shí)現(xiàn)
在類第一次加載時(shí),就進(jìn)行對(duì)象的實(shí)例化。
public class SingletonDemo {private final static SingletonDemo mSingletonDemo = new SingletonDemo();private SingletonDemo() {}public static SingletonDemo getInstance() {return mSingletonDemo;}} 復(fù)制代碼懶漢實(shí)現(xiàn)
在類加載時(shí)不進(jìn)行對(duì)象的實(shí)例化,只在對(duì)象被第一次訪問(wèn)時(shí),才進(jìn)行對(duì)象的實(shí)例化。
public class SingletonDemo {private static SingletonDemo mSingletonDemo;private SingletonDemo() {}public static SingletonDemo getInstance() {if(mSingletonDemo == null) {mSingletonDemo = new SingletonDemo();}return mSingletonDemo;}} 復(fù)制代碼明顯,在多線程的環(huán)境下,上面兩種實(shí)現(xiàn)方式都不是線程安全的。為了實(shí)現(xiàn)線程安全,我們首先可以想到使用synchronized關(guān)鍵字。
線程安全的懶漢模式
public class SingletonDemo {private static SingletonDemo mSingletonDemo;private SingletonDemo() {}public static synchronized SingletonDemo getInstance() {if(mSingletonDemo == null) {mSingletonDemo = new SingletonDemo();}return mSingletonDemo;}} 復(fù)制代碼關(guān)于synchronized關(guān)鍵字說(shuō)明一下,synchronized聲明的靜態(tài)方法,同時(shí)只能被一個(gè)執(zhí)行線程訪問(wèn),但是其他線程可以訪問(wèn)這個(gè)對(duì)象的非靜態(tài)方法。即:兩個(gè)線程可以同時(shí)訪問(wèn)一個(gè)對(duì)象的兩個(gè)不同的synchronized方法,其中一個(gè)是靜態(tài)方法,一個(gè)是非靜態(tài)方法。
所以,當(dāng)有多個(gè)線程同時(shí)訪問(wèn)getInstance靜態(tài)方法時(shí),多個(gè)其他的線程只能等待,這時(shí)只有一個(gè)線程能夠訪問(wèn)getInstance方法,等這個(gè)線程釋放后其他線程才能訪問(wèn)。這樣就會(huì)影響速度和效率。
為了提高懶漢模式的速度和效率,可以減小鎖的粒度和次數(shù)。
雙重校驗(yàn)鎖法
public class SingletonDemo {private static SingletonDemo mSingletonDemo;private SingletonDemo() {}public static synchronized SingletonDemo getInstance() {if(mSingletonDemo == null) {synchronized (SingletonDemo.class) {if(mSingletonDemo == null) {mSingletonDemo = new SingletonDemo();}}}return mSingletonDemo;}} 復(fù)制代碼從上面可以看到,只有在第一次訪問(wèn)時(shí)才會(huì)鎖定和創(chuàng)建類的對(duì)象,之后的訪問(wèn)都是直接使用已經(jīng)創(chuàng)建好的對(duì)象,這樣減少鎖定的次數(shù)和范圍,以達(dá)到提高單例模式的效率。
但是,對(duì)象的實(shí)例化,并不是一個(gè)原子性操作。即第11行代碼處,它可以分成下面三個(gè)步驟: 1、new SingletonDemo(),為SingletonDemo實(shí)例分配內(nèi)存 2、調(diào)用SingletonDemo的構(gòu)造器,完成初始化工作 3、將mSingletonDemo指向分配的內(nèi)存空間
由于java處理器可以亂序執(zhí)行,即無(wú)法保證2和3的執(zhí)行順序。這對(duì)雙重校驗(yàn)鎖法實(shí)現(xiàn)的單例模式有什么影響呢? 當(dāng)?shù)谝粋€(gè)線程訪問(wèn)getInstance方法時(shí),會(huì)鎖定臨界區(qū)(第9行到第13行代碼),它實(shí)例化對(duì)象的順序是1=>3=>2,而在這時(shí)如果有第二個(gè)線程來(lái)訪問(wèn)getInstance方法,由于第一個(gè)線程在處理器中執(zhí)行完了3未執(zhí)行2,第二個(gè)線程會(huì)馬上得到實(shí)例對(duì)象,因?yàn)榈谝粋€(gè)線程的3已經(jīng)執(zhí)行完即mSingletonDemo已經(jīng)不為空。當(dāng)?shù)诙€(gè)線程使用沒(méi)有初始化的對(duì)象時(shí)就會(huì)出現(xiàn)問(wèn)題。
所以,雙重校驗(yàn)鎖法也不是完美的,在并發(fā)環(huán)境下依然可能出現(xiàn)問(wèn)題。
靜態(tài)內(nèi)部類實(shí)現(xiàn)
public class SingletonDemo {private static SingletonDemo mSingletonDemo;private SingletonDemo() {}private static class SingletonHolder {private static final SingletonDemo INSTANCE = new SingletonDemo();}public static SingletonDemo getInstance() {return SingletonHolder.INSTANCE;}} 復(fù)制代碼第一次加載SingletonDemo類時(shí)并不會(huì)實(shí)例化INSTANCE,只有在第一次調(diào)用getInstance方法時(shí),才會(huì)加載SingletonHolder內(nèi)部類,創(chuàng)建SingletonDemo實(shí)例。這種方式不僅確保了線程安全,也保證單例對(duì)象的唯一性,同時(shí)也實(shí)現(xiàn)了單例對(duì)象的懶加載。
枚舉實(shí)現(xiàn)
上面幾種實(shí)現(xiàn)方式,可能會(huì)因?yàn)榉葱蛄谢鴦?chuàng)建新的實(shí)例,所以必須重寫readResolve方法,在readResolve方法中返回已經(jīng)創(chuàng)建的單例。
使用枚舉可以很簡(jiǎn)單的實(shí)現(xiàn)單例模式,這也是Effective Java中提倡的方式。因?yàn)槊杜e本身就是類型安全的,并且枚舉實(shí)例在任何情況下都是單例。
public enum SingletonEnumDemo {INSTANCE;public void justDoYourThing() {} } 復(fù)制代碼枚舉單例使用
SingletonEnumDemo.INSTANCE.justDoYourThing(); 復(fù)制代碼容器實(shí)現(xiàn)
public class SingletonDemo {private static Map<String, Object> singletonMap = new HashMap<String, Object>();private SingletonDemo() {}public static void registerService(String key, Object instance) {if (!singletonMap.containsKey(key)) {singletonMap.put(key, instance);}}public static Object getService(String key) {return singletonMap.get(key);}} 復(fù)制代碼轉(zhuǎn)載于:https://juejin.im/post/5ac4458151882555867fa2ed
總結(jié)
以上是生活随笔為你收集整理的23种设计模式之《单例模式》的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 使用第三方《UITableView+FD
- 下一篇: asp.net ajax控件工具集 Au