作者:ivm
之前做android 項(xiàng)目時(shí)崔赌,用的最多的就是設(shè)計(jì)模式,就是單例模式耸别,用的時(shí)候健芭,心里總有些疑問(wèn)。今天呢秀姐,看了《Android 源碼設(shè)計(jì)模式 解析與實(shí)戰(zhàn)》慈迈,把自己的總結(jié)分享一下。
首先什么是單例模式省有?
單例模式:使用時(shí)痒留,單例的對(duì)象必須保證只有一個(gè)實(shí)例存在,不予許自由構(gòu)造對(duì)象蠢沿。
那么單例模式的使用場(chǎng)景有哪些伸头?
確保某個(gè)類(lèi)有且只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例
如何“單例”舷蟀?
1.構(gòu)造函數(shù)不對(duì)外開(kāi)放熊锭,一般為private 弧轧。
2.通過(guò)一個(gè)靜態(tài)方法或者枚舉返回單例類(lèi)對(duì)象。
3.確保單例類(lèi)的對(duì)象在反序列化時(shí)不會(huì)重新構(gòu)建對(duì)象碗殷。(下文會(huì)講)
4.確保單例類(lèi)對(duì)象有且只有一個(gè),尤其是多線程下速缨。
單例模式的分類(lèi)
1.餓漢式
2.懶漢式
懶漢式與餓漢式的區(qū)別:
懶漢式:實(shí)名一個(gè)靜態(tài)對(duì)象锌妻,并且在用戶第一次調(diào)用getInstance 時(shí)進(jìn)行初始化,優(yōu)點(diǎn):?jiǎn)卫挥性谑褂脮r(shí)才會(huì)被實(shí)例化旬牲,一定程度上節(jié)約了資源仿粹。缺點(diǎn):是第一次加載時(shí)需要及時(shí)進(jìn)行實(shí)例化,反應(yīng)稍慢原茅,最大的問(wèn)題是每次調(diào)用getInstance 都進(jìn)行同步吭历,造成不必要的同步開(kāi)銷(xiāo)。一般不建議這么用擂橘。
餓漢式:是在聲明靜態(tài)對(duì)象時(shí)就已經(jīng)初始化晌区。
3.Double Check Lock (DCL)實(shí)現(xiàn)單例
DCL ?介紹
DCL 在getInstance 方法中 對(duì)instance 進(jìn)行兩次判空:相信很多人對(duì)此都有些疑惑。為什么要判斷兩次通贞,第一個(gè)判空是為了避免不必要的同步朗若,第二層判斷是為了在null 情況下創(chuàng)建實(shí)例。instance=new Singleton(); 語(yǔ)句看起來(lái)是有代碼昌罩,單實(shí)際是一個(gè)原子操作哭懈,最終會(huì)被編譯成多條匯編指令,大致做了三件事:
1.給Singleton 分配內(nèi)存
2.調(diào)用Singleton 的構(gòu)造函數(shù)茎用,初始化成員字段
3.將instance 對(duì)象指向分配的內(nèi)存空間(此時(shí)instance 就不是null 了)
但是jdk 1.5 以后java 編譯器允許亂序執(zhí)行?遣总。所以執(zhí)行順序可能是1-3-2 或者 1-2-3.如果是前者先執(zhí)行3 的話 切換到其他線程,instance 此時(shí) 已經(jīng)是非空了轨功,此線程就會(huì)直接取走instance 旭斥,直接使用,這樣就回出錯(cuò)夯辖。DCL 失效琉预。解決方法 SUN 官方已經(jīng)給我們了。將instance 定義成 privatevolatilestatic Singleton instance =null: 即可
DCL 的優(yōu)點(diǎn)蒿褂,資源利用率高圆米,第一次執(zhí)行g(shù)etInstance 時(shí)才會(huì)被實(shí)例化,效率高啄栓。缺點(diǎn):第一次加載反應(yīng)慢娄帖,也由于java 內(nèi)存 模型的原因偶爾會(huì)失敗,在高并發(fā)環(huán)境下昙楚,有一定缺陷近速,雖然發(fā)生概率很小。(很常用)
4.靜態(tài)內(nèi)部類(lèi)單例模式
加載singleton 類(lèi)時(shí)不會(huì)初始化instance 只有在調(diào)用getInstance 方法時(shí),才會(huì)導(dǎo)致instance 被初始化,這個(gè)方法不僅能夠確保線程安全削葱,也能夠保證 單例對(duì)象的唯一性,同時(shí)也延遲了單例的實(shí)例化奖亚,是推薦使用的單例模式實(shí)現(xiàn)方式。
5.枚舉單例
它在任何情況下都是單例的析砸,也是最簡(jiǎn)單的昔字。在上述的幾種單例模式下,都會(huì)有一種情況首繁,它們會(huì)出現(xiàn)重新創(chuàng)建對(duì)象的情況作郭,那就是反序列化。
要杜絕單例對(duì)象在反序列化時(shí)重新生成對(duì)象弦疮,那么必須加入如下方法:
但是枚舉就不必要加這個(gè)方法夹攒,因?yàn)榉葱蛄性捤膊粫?huì)生成新的實(shí)例。
6.使用容器模式實(shí)現(xiàn)單例
將眾多單例模式類(lèi)型注入到一個(gè)統(tǒng)一的管理類(lèi)中胁塞,在使用時(shí)根據(jù)key 對(duì)應(yīng)類(lèi)型的對(duì)象咏尝。這種方式使得我們可以管理多種類(lèi)型的單例,并且在使用時(shí)可以通過(guò)統(tǒng)一的接口進(jìn)行獲取操作闲先,降低了用戶的使用成本状土,也對(duì)用戶隱藏了具體實(shí)現(xiàn),降低了耦合度伺糠。