標(biāo)簽: 設(shè)計(jì)模式初涉
描述性文字:
有23種不同的設(shè)計(jì)模式,分為三大類(lèi):
本節(jié)描述的是最簡(jiǎn)單爛大街的單例模式奶稠。
1.應(yīng)用場(chǎng)景:
當(dāng)需要保證類(lèi)在內(nèi)存中的對(duì)象唯一性糙箍,可以使用單例模式讲仰,
不想創(chuàng)建多個(gè)實(shí)例浪費(fèi)資源谜诫,或者避免多個(gè)實(shí)例由于多次調(diào)用
而出現(xiàn)錯(cuò)誤漾峡。一般寫(xiě)工具類(lèi),線(xiàn)程池喻旷,緩存生逸,數(shù)據(jù)庫(kù)等可以用到。
2.設(shè)計(jì)思想:(保證對(duì)象的唯一性的三個(gè)步驟)
想法 | 實(shí)現(xiàn) |
---|---|
不允許其他程序用new對(duì)象 | 私有化該類(lèi)的構(gòu)造函數(shù) |
在該類(lèi)中創(chuàng)建對(duì)象 | 通過(guò)new在本類(lèi)中創(chuàng)建一個(gè)本類(lèi)對(duì)象 |
對(duì)外提供一個(gè)可以讓其他程序獲取該對(duì)象的方法 | 定義一個(gè)公有的方法且预,將在該類(lèi)中所創(chuàng)建的對(duì)象返回 |
3.各種各樣的單例寫(xiě)法
1.餓漢模式
public class Singleton () {
private static Singleton instance = new Singleton()
private Singleton(){ }
public static Singleton getInstance() {
return instance;
}
}
獲取單例對(duì)象:Singleton instace = Singleton.getInstance();
優(yōu)點(diǎn):類(lèi)加載的時(shí)候就完成實(shí)例化槽袄,避免線(xiàn)程同步問(wèn)題
缺點(diǎn):由于在類(lèi)加載的時(shí)候就進(jìn)行了實(shí)例化,所以沒(méi)有達(dá)到Lazy Loading(懶加載)
的效果锋谐,即使我們沒(méi)用到這個(gè)實(shí)例遍尺,但是他還是會(huì)加載,從而造成內(nèi)存浪費(fèi)(可以忽略涮拗,
最常用的一種)乾戏。
2.懶漢模式(線(xiàn)程不安全,不可用)
public class Singleton {
private static Singleton instance = null;
private Singleton() { }
private static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
盡管達(dá)到了懶加載多搀,但是卻存在線(xiàn)程安全問(wèn)題歧蕉,比如有兩個(gè)線(xiàn)程灾部,剛好都
執(zhí)行完if(instance == null)康铭,接著準(zhǔn)備執(zhí)行instance = new Singleton()
語(yǔ)句,這樣的結(jié)果會(huì)導(dǎo)致我們實(shí)例化了兩個(gè)Singleton對(duì)象赌髓,這就是懶漢單例
模式可能會(huì)引發(fā)的線(xiàn)程安全問(wèn)題从藤,解決這個(gè)方法,我們可以對(duì)getInstance方法加鎖锁蠕。
3.懶漢模式(線(xiàn)程安全夷野,但效率低,不推薦使用)
public class Singleton {
private Singleton instance = null;
private Singleton() { }
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
盡管保證了線(xiàn)程安全荣倾,但是每個(gè)線(xiàn)程在想要獲得實(shí)例時(shí)悯搔,執(zhí)行g(shù)etInstance()
方法都需要進(jìn)行同步,而實(shí)例化代碼只需執(zhí)行一次就夠了舌仍,后面獲取該實(shí)例妒貌,
直接return即可通危,方法進(jìn)行同步效率太低,需要改進(jìn)灌曙。還有一種寫(xiě)法是:
synchronized (Singleton.class) { instance = new Singleton(); }
這樣一樣是線(xiàn)程不安全的菊碟,如果你想使用懶漢模式的話(huà),推薦使用下面的:
DCL單例(雙重檢查鎖定)在刺。
4.懶漢模式雙重校驗(yàn)鎖
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() { }
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
代碼中進(jìn)行了兩次if檢查,這樣就可以保證線(xiàn)程安全魄幕,初始化一次后隙姿,
后面再次訪(fǎng)問(wèn)時(shí)输玷,if檢查,直接return 實(shí)例化對(duì)象机久。volatile是1.5后
引入的膘盖,volatile關(guān)鍵字會(huì)屏蔽Java虛擬機(jī)所做的一些代碼優(yōu)化侠畔,會(huì)導(dǎo)
致系統(tǒng)運(yùn)行效率降低损晤,而更好的寫(xiě)法是使用靜態(tài)內(nèi)部類(lèi)來(lái)實(shí)現(xiàn)單例尤勋!
5.靜態(tài)內(nèi)部類(lèi)實(shí)現(xiàn)單例模式
public class Singleton {
private Singleton() { }
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
和餓漢式類(lèi)似最冰,兩者都是通過(guò)類(lèi)裝載機(jī)制來(lái)保證初始化實(shí)例
的時(shí)候只有一個(gè)線(xiàn)程,從而避免線(xiàn)程安全問(wèn)題赌朋,餓漢式的
Singleton類(lèi)被加載沛慢,就會(huì)實(shí)例化,而靜態(tài)內(nèi)部類(lèi)這種斩熊,
當(dāng)Singleton類(lèi)被加載時(shí)粉渠,不會(huì)立即實(shí)例化霸株,調(diào)用getInstance方法集乔,
才會(huì)裝載SingletonHolder類(lèi),從而完成Singleton的實(shí)例化尤溜。
6.枚舉實(shí)現(xiàn)單例模式
public enum SingletonEnum {
INSTANCE;
private Singleton instance;
SingletonEnum() {
instance = new Singleton()
}
public Singleton getInstance() {
return instance;
}
}
訪(fǎng)問(wèn)方式:SingletonEnum.INSTANCE.method();
INSTANCE即為SingletonEnum類(lèi)型的引用宫莱,得到它就可以調(diào)用
枚舉中的方法授霸。既避免了線(xiàn)程安全問(wèn)題碘耳,還能防止反序列化
重新創(chuàng)建新的對(duì)象框弛,但是失去了類(lèi)的一些特性,沒(méi)有延時(shí)加載愉阎,
推薦使用力奋。
7.使用容器實(shí)現(xiàn)單例模式
public class SingletonManager {
private static Map<String,Object> objMap = new HashMap<String,Object>();
private Singleton() { }
public static void registerService(String key,Object instance) {
if(!objMap.containsKey(key)) {
objMap.put(key,instance);
}
}
public static Object getService(String key) {
return objMap.get(key);
}
}
將多種單例類(lèi)型注入到一個(gè)統(tǒng)一的管理類(lèi)中景殷,在使用時(shí)根據(jù)key獲取對(duì)象
對(duì)應(yīng)類(lèi)型的對(duì)象。這種方式使得我們可以管理多種類(lèi)型的單例咐旧,并且在使
用時(shí)可以通過(guò)統(tǒng)一的接口進(jìn)行獲取操作铣墨,降低了用戶(hù)的使用成本办绝,也對(duì)用
戶(hù)隱藏了具體實(shí)現(xiàn),降低了耦合度屡律。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):保持類(lèi)對(duì)象唯一性超埋,對(duì)于頻繁創(chuàng)建和銷(xiāo)毀的對(duì)象可以提高性能佳鳖。
缺點(diǎn):擴(kuò)展困難,單例的方法無(wú)法生成子類(lèi)對(duì)象繁成,要擴(kuò)展的話(huà)基本要重寫(xiě)這個(gè)類(lèi)巾腕。
注意事項(xiàng):
在Android中如果單例對(duì)象持有Context絮蒿,那么很容易引發(fā)內(nèi)存泄露。
此時(shí)需要注意傳遞給單例對(duì)象的Context最好是Application Context!!!
本文代碼:
https://github.com/coder-pig/DesignPatternsExample/tree/master/1.Singleton%20Pattern
修改日志:
- 2017.1.24 增加設(shè)計(jì)模式思維導(dǎo)圖佛寿,添加本文代碼鏈接