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