單例模式也被成為單件模式(或單體模式),主要作用是控制某個(gè)類型的實(shí)例數(shù)量為一個(gè),而且只有一個(gè)。
很多時(shí)候,我們需要在應(yīng)用中保存一個(gè)唯一的實(shí)例,例如后臺(tái)服務(wù)進(jìn)程需要一個(gè)全局的計(jì)數(shù)器,記錄用戶訪問各個(gè)頁面的點(diǎn)擊數(shù)量。如果計(jì)數(shù)器數(shù)量非常多,那么必然會(huì)在計(jì)數(shù)過程中出現(xiàn)沖突的情況。為了盡量減少?zèng)_突,一個(gè)最簡單的辦法是只有一個(gè)計(jì)數(shù)器實(shí)例,所有計(jì)數(shù)工作都通過它來完成。
單例模式實(shí)現(xiàn)方式
實(shí)現(xiàn)單例模式的方式有很多,大體上可以分為外部方式和內(nèi)部方式兩種。
(1)外部方式
外部方式就是外部的客戶程序使用某些全局對象時(shí),做一些“Try-User”工作,簡單說就是試著獲取對象實(shí)例,如果沒有,會(huì)獲取不到,就自己創(chuàng)建一個(gè),然后把它放在全局的位置上,然后再用,如果本來就有,則直接拿一個(gè)現(xiàn)成的來用。
(2)內(nèi)部方式
內(nèi)部方式就是類內(nèi)部自己控制生成實(shí)例的數(shù)量,無論外部程序是否嘗試過,類型自己先生成一個(gè)實(shí)例,然后控制只提供這一個(gè)實(shí)例,客戶程序使用的都是這一個(gè)現(xiàn)成的實(shí)例。
但是隨著集群和多核技術(shù)的普及,想通過簡單的類型內(nèi)部控制實(shí)現(xiàn)真正的單例模式越來越難,通過經(jīng)典的單例模式實(shí)現(xiàn)分布式環(huán)境下的單例并不現(xiàn)實(shí)。因此本文所說的單例是有范圍限制的。
單利模式特點(diǎn)
單例模式和工廠模式一樣,是一種對象的創(chuàng)建模式。有如下幾個(gè)特點(diǎn):
1)該類只有一個(gè)實(shí)例
2)該類在自己內(nèi)部自行創(chuàng)建自身的實(shí)例對象
3)向整個(gè)系統(tǒng)公開或者這個(gè)實(shí)例的接口
簡單的單例模式代碼實(shí)現(xiàn)如下:

可以看到,代碼中定義了一個(gè)私有的構(gòu)造器,不允許外界訪問,這樣外界無法通過new創(chuàng)建該類對象,只能在類內(nèi)部new。instance就是在類內(nèi)部實(shí)例化的一個(gè)對象,可以想到在全系統(tǒng)中也只有這一個(gè),然后再通過getInstance這個(gè)靜態(tài)方法獲?。?/p>
Demo demo = Demo.getInstance();
Demo單例類在類被加載時(shí),instance就會(huì)被初始化,即便加載器是靜態(tài)的。如果初始化一個(gè)單例對象需要的開銷大,那么在使用之前最合理的做法是不實(shí)例化,將實(shí)例化這個(gè)動(dòng)作推遲到使用的時(shí)候,這就是惰性加載,惰性加載常常用于需要加載大量數(shù)據(jù)的單體。簡單修改后的代碼如下:

可以看到只是加了一個(gè)是否為null的判斷,惰性加載的使用方法與普通加載完全一樣。
單例模式的功能
單例模式的功能是保證這個(gè)類在運(yùn)行期間只會(huì)被創(chuàng)建一個(gè)類實(shí)例,另外單例模式還提供了一個(gè)全局唯一訪問這個(gè)類實(shí)例的訪問點(diǎn),就是getInstance方法。不管是上面的普通加載還是惰性加載,這個(gè)全局的唯一訪問點(diǎn)是一樣的。對于單例模式而言,不管采用何種實(shí)現(xiàn)方式,它都只關(guān)心類實(shí)例的創(chuàng)建問題,并不關(guān)心具體的業(yè)務(wù)功能。
單例模式的范圍
單例類在什么范圍內(nèi)可以叫單例類? 目前Java里面實(shí)現(xiàn)的單例是一個(gè)ClassLoader及其子ClassLoader的范圍。因?yàn)镃lassLoader在普通加載模式下實(shí)現(xiàn)單例類時(shí),會(huì)相應(yīng)的創(chuàng)建一個(gè)類實(shí)例。
這說明如果一個(gè)虛擬機(jī)里面有很多ClassLoader,而且這些ClassLoader都裝載某個(gè)類的話,就算這個(gè)類是單例,它也會(huì)產(chǎn)生很多個(gè)實(shí)例。如果一個(gè)機(jī)器上有多個(gè)虛擬機(jī),那么每個(gè)虛擬機(jī)里面都至少有一個(gè)這個(gè)類的實(shí)例。也就是整個(gè)機(jī)器上就有很多個(gè)實(shí)例,這個(gè)類更不會(huì)是單例了。
另外需要注意,普通的單例模式并不適用于集群環(huán)境。
單例模式的命名
一般建議獲取單例的方法命名為getInstance(),看到這個(gè)方法就知道這個(gè)方法返回類型肯定是單例類的實(shí)例了。getInstance方法可以有參數(shù),要根據(jù)私有構(gòu)造器的具體情況確定,在大多數(shù)情況下是沒有參數(shù)的。單例模式的名稱因?yàn)榉g的不同,也可以叫單件模式,單體模式等等。
單例模式的種類大致分為三種,分別是懶漢式單例,餓漢式單例和登記式單例。在下面的內(nèi)容中會(huì)分別介紹。