單件模式,也叫單例模式,可以說(shuō)是設(shè)計(jì)模式中最簡(jiǎn)單的一種。顧名思義,就是創(chuàng)造獨(dú)一無(wú)二的唯一的一個(gè)實(shí)例化的對(duì)象。
為什么要這樣做呢?因?yàn)橛行r(shí)候,我們只需要一個(gè)對(duì)象就夠了,太多對(duì)象反而會(huì)引起不必要的麻煩。比如說(shuō),線程池,緩存,打印機(jī),注冊(cè)表,如果存在多個(gè)實(shí)例的話,反而會(huì)導(dǎo)致許多問(wèn)題!
引出單例模式
我們通過(guò)一個(gè)小問(wèn)題引出單例模式!
- 如何創(chuàng)建一個(gè)對(duì)象?我們都知道
new MyObject(); - 當(dāng)我們需要?jiǎng)?chuàng)建與另外一個(gè)對(duì)象時(shí),只需要再次
new MyObject();即可 - 那么如下這樣的代碼是正確的么?
public MyClass{
private MyClass() {}
}
- 看過(guò)去這是合法的定義,沒(méi)有什么語(yǔ)法錯(cuò)誤。但仔細(xì)想想,含有私有構(gòu)造器的話,只能在MyClass內(nèi)調(diào)用構(gòu)造器。因?yàn)楸仨氂蠱yclass的實(shí)例才能調(diào)用構(gòu)造器,但因?yàn)闆](méi)有其他類可以取得它的實(shí)例,所以,我們無(wú)法實(shí)例化它,這像不像雞生蛋還是蛋生雞的問(wèn)題?哈哈哈
- 為了解決這個(gè)問(wèn)題,取得MyClass類的實(shí)例,我們創(chuàng)造一個(gè)靜態(tài)方法
public MyClass{
private MyClass() {}
public static MyClass getInstance() {
return new MyClass();
}
}
- 我們添加了一個(gè)靜態(tài)的類方法,它可以返回一個(gè)對(duì)象實(shí)例,由于他是public,所以外部可以調(diào)用他。這實(shí)際上就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的單例模式。
經(jīng)典單例模式的實(shí)現(xiàn)
public class Singleton {
private static Singleton uniqueInstance;
private Singleton(){}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
- 這里實(shí)現(xiàn)了一個(gè)概念,叫延遲實(shí)例化(lazy instance)。因?yàn)樵谖覀儾恍枰獙?shí)例的時(shí)候,這個(gè)實(shí)例就永遠(yuǎn)不會(huì)被實(shí)例化。
定義單件模式
單件模式的定義: 確保一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)全局訪問(wèn)點(diǎn)。
這定義應(yīng)該很好理解,我們結(jié)合類圖說(shuō)明:

Paste_Image.png
經(jīng)典單件模式存在的問(wèn)題
經(jīng)典單件模式實(shí)際中存在這一定的問(wèn)題,在第一次初始化實(shí)例的時(shí)候,如果同時(shí)有不同的線程訪問(wèn),那么可能最后不只實(shí)例化出一個(gè)對(duì)象。

Paste_Image.png
如圖所示,如果兩個(gè)線程如圖所示的順序交錯(cuò)執(zhí)行,那么最后會(huì)實(shí)例化兩個(gè)對(duì)象!
這就是經(jīng)典單例模式存在的多線程問(wèn)題。
解決單例模式的多線程問(wèn)題
synchronize
顯然最簡(jiǎn)單的一種解決方法就是同步getInstance方法。
public class Singleton {
private static Singleton uniqueInstance;
private Singleton(){}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
這樣顯然可以很好的解決問(wèn)題,但是同步會(huì)降低效率。
急切實(shí)例化
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton(){}
public static synchronized Singleton getInstance() {
return uniqueInstance;
}
}
在任何線程訪問(wèn)uniqueInstance變量前,我們保證一定已經(jīng)創(chuàng)建了這個(gè)實(shí)例。
雙重檢查加鎖
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}