單例模式

1) 單例的實(shí)現(xiàn):
線程安全(加鎖):
上述代碼中的一個(gè)缺點(diǎn)是該類加載的時(shí)候就會(huì)直接new 一個(gè)靜態(tài)對(duì)象出來,當(dāng)系統(tǒng)中這樣的類較多時(shí),會(huì)使得啟動(dòng)速度變慢?,F(xiàn)在流行的設(shè)計(jì)都是講“延遲加載”,我們可以在第一次使用的時(shí)候才初始化第一個(gè)該類對(duì)象。所以這種適合在小系統(tǒng)。
如果所使用的公用變量在多線程下沒有被保護(hù)機(jī)制時(shí),變量結(jié)果會(huì)和理論值不一致,這樣就叫作線程不安全,相反公用變量在保護(hù)機(jī)制下工作,就不會(huì)出現(xiàn)未知變化,那這樣線程就是安全的.
單例中有個(gè)部分,就是有個(gè)對(duì)象作為這個(gè)類的成員變量被保存,而不是作為局部變量,所以其他方法發(fā)生并發(fā)訪問這個(gè)對(duì)象時(shí)其實(shí)是在操作同一個(gè)對(duì)象。
舉個(gè)例子,兩個(gè)人同時(shí)調(diào)用一個(gè)方法(給我蛋糕),但這個(gè)方法返回一個(gè)蛋糕的單例對(duì)象,兩個(gè)人同時(shí)獲得了同一個(gè)蛋糕,并坐下,舉起刀叉,結(jié)果第一個(gè)人先吞了蛋糕,就造成了第二個(gè)人明明得到了蛋糕,卻沒能吃到這個(gè)結(jié)果。
Tomcat可以用單例預(yù)熱。
傳統(tǒng)單例可以節(jié)省內(nèi)存

為了線程安全:(使用同步鎖)

單鎖與雙鎖:

上述代碼中的一次鎖住了一個(gè)方法, 這個(gè)粒度有點(diǎn)大,改進(jìn)就是只鎖住其中的new語句就OK。就是所謂的“雙重鎖”機(jī)制:

管理上的單例實(shí)現(xiàn):(登記式單例)

本例子中利用Map里面存儲(chǔ)創(chuàng)建對(duì)象的id來阻止對(duì)象重復(fù)實(shí)例化。
什么時(shí)候用單例?無狀態(tài)的對(duì)象都可以作為單例。無數(shù)據(jù)的類似Service(無id那些),Dao是可以作為單例的,有數(shù)據(jù)的,如UserBean不可以。(如打印機(jī))。還有類只有只讀數(shù)據(jù)(無set方法)可以作為單例:

還有如下應(yīng)用情景:
1.Windows的TaskManager(任務(wù)管理器)就是很典型的單例模式(這個(gè)很熟悉吧),想想看,是不是呢,你能打開兩個(gè)windowstask manager嗎?不信你自己試試看哦~
2.windows的RecycleBin(回收站)也是典型的單例應(yīng)用。在整個(gè)系統(tǒng)運(yùn)行過程中,回收站一直維護(hù)著僅有的一個(gè)實(shí)例。
3. 網(wǎng)站的計(jì)數(shù)器,一般也是采用單例模式實(shí)現(xiàn),否則難以同步。
4. 應(yīng)用程序的日志應(yīng)用,一般都何用單例模式實(shí)現(xiàn),這一般是由于共享的日志文件一直處于打開狀態(tài),因?yàn)橹荒苡幸粋€(gè)實(shí)例去操作,否則內(nèi)容不好追加。
5. Web應(yīng)用的配置對(duì)象的讀取,一般也應(yīng)用單例模式,這個(gè)是由于配置文件是共享的資源。
6. 數(shù)據(jù)庫連接池的設(shè)計(jì)一般也是采用單例模式,因?yàn)閿?shù)據(jù)庫連接是一種數(shù)據(jù)庫資源。數(shù)據(jù)庫軟件系統(tǒng)中使用數(shù)據(jù)庫連接池,主要是節(jié)省打開或者關(guān)閉數(shù)據(jù)庫連接所引起的效率損耗,這種效率上的損耗還是非常昂貴的,因?yàn)楹斡脝卫J絹砭S護(hù),就可以大大降低這種損耗。
7. 多線程的線程池的設(shè)計(jì)一般也是采用單例模式,這是由于線程池要方便對(duì)池中的線程進(jìn)行控制。
8. 操作系統(tǒng)的文件系統(tǒng),也是大的單例模式實(shí)現(xiàn)的具體例子,一個(gè)操作系統(tǒng)只能有一個(gè)文件系統(tǒng)。
9. HttpApplication也是單位例的典型應(yīng)用。熟悉ASP.Net(IIS)的整個(gè)請(qǐng)求生命周期的人應(yīng)該知道HttpApplication也是單例模式,所有的HttpModule都共享一個(gè)HttpApplication實(shí)例.
10.說到門面類,就不能不提門面(Facade)模式??蛻舳伺c多個(gè)子系統(tǒng)的通信必須通過一個(gè)統(tǒng)一的門面(Facade)對(duì)象進(jìn)行,這就是門面模式。這個(gè)統(tǒng)一的門面(Facade)對(duì)象就是門面類。在門面模式中,通常只需要一個(gè)門面類,并且此門面類只有一個(gè)實(shí)例,換言之它是一個(gè)單例類。但這不是絕對(duì)的。
寫單例時(shí)通用的規(guī)則:
1. 使類變成單例必須用static存放常量池。
一般在方法內(nèi)定義:
是線程安全的,變量在棧中有個(gè)引用指向堆里的對(duì)象。
而向管理上的單例:
Map放在常量池在堆中,可以為多個(gè)線程訪問,線程不安全。
Servlet是單例的,在Servlet里面用privateString name;之類會(huì)因?yàn)閐o/get方法引發(fā)線程安全問題。第三方類庫要了解api可不可以做成單例。(有方法修改數(shù)據(jù)就不可作為單例)
單例模式也是一種比較常見的設(shè)計(jì)模式,它到底能帶給我們什么好處呢?其實(shí)無非是三個(gè)方面的作用:
第一、控制資源的使用,通過線程同步來控制資源的并發(fā)訪問;
第二、控制實(shí)例產(chǎn)生的數(shù)量,達(dá)到節(jié)約資源的目的。
第三、作為通信媒介使用,也就是數(shù)據(jù)共享,它可以在不建立直接關(guān)聯(lián)的條件下,讓多個(gè)不相關(guān)的兩個(gè)線程或者進(jìn)程之間實(shí)現(xiàn)通信。
比如,數(shù)據(jù)庫連接池的設(shè)計(jì)一般采用單例模式,數(shù)據(jù)庫連接是一種數(shù)據(jù)庫資源
兩種單例:
1. 餓漢式
public class EagerSingleton
{
private static final EagerSingleton m_instance = newEagerSingleton();
/**
* 私有的默認(rèn)構(gòu)造子
*/
private EagerSingleton() { }
/**
* 靜態(tài)工廠方法
*/
public static EagerSingleton getInstance()
{
return m_instance;
}
}
2. 懶漢式
public class LazySingleton
{
private static LazySingleton m_instance = null;
/**
* 私有的默認(rèn)構(gòu)造子,保證外界無法直接實(shí)例化
*/
private LazySingleton() { }
/**
* 靜態(tài)工廠方法,返還此類的惟一實(shí)例
*/
synchronized public static LazySingleton getInstance()
{
if(m_instance == null)
{
m_instance = newLazySingleton();
}
return m_instance;
}
}
餓漢式是線程安全的,在類創(chuàng)建的同時(shí)就已經(jīng)創(chuàng)建好一個(gè)靜態(tài)的對(duì)象供系統(tǒng)使用,以后不在改變
懶漢式如果在創(chuàng)建實(shí)例對(duì)象時(shí)不加上synchronized則會(huì)導(dǎo)致對(duì)對(duì)象的訪問不是線程安全的
推薦使用第一種