俗話說(shuō):"天下大勢(shì);合久必分,分久必合",中華五千年的歷史文化,無(wú)數(shù)朝代的更迭,一次又一次的證明了這條規(guī)律的正確性;既然是規(guī)律那就一定不止在“天下大勢(shì)” 方面出現(xiàn),在很多其他的事物上也同樣有所體現(xiàn)。
比如,我們縮小一個(gè)單位量級(jí),放在一個(gè)國(guó)家的層面來(lái)說(shuō),一個(gè)國(guó)家只能同時(shí)有一個(gè)皇帝,并且皇帝這個(gè)職位,也是''風(fēng)水輪流轉(zhuǎn),今年到我家'';而且每隔幾年總有那么幾個(gè)皇帝干著干著不想干撂挑子了或者干脆被別人不想干了(被別人搶了皇位);但是無(wú)論怎樣,我們都能發(fā)現(xiàn),當(dāng)皇帝的人總是只有一個(gè)。正所謂:"一山不容二虎",否則會(huì)出大事的。
歷史上倒還真出現(xiàn)過(guò)有兩個(gè)皇帝的時(shí)刻,那就是大明王朝;這種情況就出現(xiàn)了國(guó)家安全的問(wèn)題,并不可取。
單例模式三步曲
如果把皇帝這個(gè)職業(yè)的唯一性的特點(diǎn),放到我們的軟件開(kāi)發(fā)行業(yè)的話,同樣也有相通的地方,比如:在設(shè)計(jì)模式中就有單例模式,那么何為單例模式呢:
單例模式:保證整個(gè)系統(tǒng)中一個(gè)類(lèi)只有一個(gè)對(duì)象的實(shí)例,實(shí)現(xiàn)這種功能的方式就叫單例模式。
翻譯成白話文就是,整個(gè)系統(tǒng)中有且只有一個(gè)實(shí)例對(duì)象,這樣在我們的程序中就可以共享這個(gè)對(duì)象資源了,這也是設(shè)計(jì)模式中最簡(jiǎn)單的一種設(shè)計(jì)模式,今天我們就來(lái)嘮嘮單例模式。
剛才從概念中,我們了解到了單例模式就是整個(gè)系統(tǒng)中只有一個(gè)實(shí)例對(duì)象,那么問(wèn)題來(lái)了,怎么才能保證整個(gè)系統(tǒng)中只有一個(gè)對(duì)象實(shí)例呢?這就是接下來(lái)我們重點(diǎn)要探討的。
要保證整個(gè)系統(tǒng)中對(duì)象只有一個(gè),這里就要借鑒宋丹丹老師當(dāng)年把大象裝冰箱總結(jié)出的三部曲,所以保證對(duì)象的唯一性也只需要三步。
私有化構(gòu)造器
既然對(duì)象只有一個(gè),那就不能隨便什么人都能創(chuàng)建對(duì)象了,所以這里要嚴(yán)格把控。
自己創(chuàng)建對(duì)象
既然不讓別人創(chuàng)建對(duì)象,那就只能自己實(shí)例化來(lái)創(chuàng)建對(duì)象了。
提供獲取對(duì)象的方法
因?yàn)閷?duì)象是自己創(chuàng)建的,所以我們還的提供一個(gè)方法其他人能夠拿到來(lái)使用。
單例模式實(shí)現(xiàn)
上面咱么討論了單例模式的實(shí)現(xiàn)方式,三步走;這里我們就要將理論轉(zhuǎn)化為實(shí)踐;畢竟,實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn);接下來(lái)上代碼。
單例模式之餓漢式
聽(tīng)到餓漢式這個(gè)名字,大家可能都可以想到一個(gè)饑腸轆轆的餓漢,這個(gè)時(shí)候他最需要的就是可以馬上有東西吃,所以單例模式的餓漢式就是在類(lèi)剛被加載的時(shí)候,馬上就創(chuàng)建對(duì)象,生怕?lián)尣坏匠缘摹?/div>
/**
* 單例模式之餓漢式
*/
public class SingletonHungry {
// 在類(lèi)加載的時(shí)候,就創(chuàng)建對(duì)象
private final static SingletonHungry instance = new SingletonHungry();
/**
* 私有化構(gòu)造器,防止外界創(chuàng)建對(duì)象
*/
private SingletonHungry() {
}
// 提供外界獲取對(duì)象的方法
public static SingletonHungry getInstance() {
// 返回單利對(duì)象
return instance;
}
}
餓漢式雖好,但是總感覺(jué)還是有點(diǎn)問(wèn)題,因?yàn)樗陬?lèi)剛被加載的時(shí)候,就各種創(chuàng)建對(duì)象,如果我暫時(shí)還不需要的話,這樣其實(shí)是會(huì)造成資源的浪費(fèi)的,所以還不完美,怎么來(lái)解決這個(gè)問(wèn)題呢;大家應(yīng)該經(jīng)常聽(tīng)到懶加載這個(gè)詞,它的意思就是所有的事情我都不著急,只有你需要我的時(shí)候我才出現(xiàn),這個(gè)模式就是懶漢式。
單例模式之懶漢式
懶漢式的方式就是,一開(kāi)始比較懶,不去創(chuàng)建對(duì)象,等到程序需要我的時(shí)候,實(shí)在沒(méi)法再拖了,就只能創(chuàng)建對(duì)象了。
/**
* 單例模式之懶漢式
*/
public class SingletonLazy {
// 懶漢式 需要對(duì)象的時(shí)候再去創(chuàng)建
private static final SingletonLazy instance = null;
// 私有化話構(gòu)造器
private SingletonLazy() {
}
/**
* 對(duì)外暴露的獲取單例對(duì)象的方法,
* 存在多線程并發(fā)不安全的問(wèn)題,可以給通過(guò)加鎖synchronized關(guān)鍵字 鎖會(huì)影響性能
*
* @return
*/
public SingletonLazy getInstance() {
// 判斷是否已經(jīng)創(chuàng)建了對(duì)象,沒(méi)有就創(chuàng)建,有,就直接返回
if (instance == null) {
return new SingletonLazy();
}
return instance;
}
}
這種方式看著不錯(cuò),不會(huì)有餓漢式的浪費(fèi)資源;但卻帶來(lái)了另外一個(gè)問(wèn)題,線程安全問(wèn)題,這個(gè)問(wèn)題在互聯(lián)網(wǎng)環(huán)境下的高并發(fā)的情況下,可能比資源浪費(fèi)還要嚴(yán)重,對(duì)于多線程并發(fā)帶來(lái)的安全問(wèn)題,我們可以使用加鎖synchronized關(guān)鍵字來(lái)解決。
/**
* 單例模式之懶漢式加鎖解決線程安全問(wèn)題
*/
public class SingletonLazy {
// 懶漢式 需要對(duì)象的時(shí)候再去創(chuàng)建
private static final SingletonLazy instance = null;
// 私有化話構(gòu)造器
private SingletonLazy() {
}
/**
* 對(duì)外暴露的獲取單例對(duì)象的方法
* @return
*/
public synchronized SingletonLazy getInstance() {
// 判斷是否已經(jīng)創(chuàng)建了對(duì)象,沒(méi)有就創(chuàng)建,有,就直接返回
if (instance == null) {
return new SingletonLazy();
}
return instance;
}
}
到這里,線程安全問(wèn)題也解決了,但是由于加鎖又帶來(lái)了性能的問(wèn)題;真是一波未平一波又起啊,性能問(wèn)題在也是極其重要的問(wèn)題,這里我們就要換一個(gè)思路了。
單例模式之靜態(tài)內(nèi)部類(lèi)
這里我們用靜態(tài)內(nèi)部類(lèi)的方式來(lái)實(shí)現(xiàn)單例模式
/**
* 靜態(tài)內(nèi)部類(lèi)寫(xiě)法
* * 推薦寫(xiě)法
*/
public class Singleton {
private Singleton() {
}
/**
* 靜態(tài)內(nèi)部類(lèi) 只在getInstance方法內(nèi)使用
*/
private static class SingletonHolder {
public static Singleton instance = new Singleton();
}
// 返回接口
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
這種方式跟餓漢式方式采用的機(jī)制類(lèi)似,但又有不同;兩者都是采用了類(lèi)加載的機(jī)制來(lái)保證初始化實(shí)例時(shí)只有一個(gè)線程。
不同:
餓漢式是只要Singleton類(lèi)被加載就會(huì)實(shí)例化,沒(méi)有懶加載
靜態(tài)內(nèi)部類(lèi)方式是在需要實(shí)例化時(shí),調(diào)用getInstance方法,才會(huì)加載SingletonHolder類(lèi),實(shí)例化Singleton
由于類(lèi)的靜態(tài)屬性只會(huì)在第一次加載類(lèi)的時(shí)候初始化,所以在這里我們也保證了線程的安全性,所以通過(guò)這種靜態(tài)內(nèi)部類(lèi)的方式解決了資源浪費(fèi)和性能的問(wèn)題。
靜態(tài)資源內(nèi)部類(lèi)的方式也是我們比較推薦的一種寫(xiě)法哦。
![]() |