單例是一種設(shè)計(jì)模式,單例模式是在什么情況下產(chǎn)生的呢?
在Java中每個(gè)東西都看作對象,若此時(shí)有如下情況,教室中的黑板(對象A)可以供教師(對象B)和學(xué)生(對象C)書寫,此時(shí)黑板就是老師和學(xué)生的共享對象,而單例模式就如此生成了。將黑板設(shè)計(jì)為單例,則同時(shí)教師和學(xué)生都能共享。
單例模式的特點(diǎn)
1、單例的類在系統(tǒng)里只有一個(gè)實(shí)例
2、單例的類能夠?qū)崿F(xiàn)自動的實(shí)例化
3、單例的類在對整個(gè)系統(tǒng)可見的,因此是public的
什么情況下將對象設(shè)置為單例
1、若某個(gè)對象被實(shí)例化很頻繁,然后銷毀的話,將此對象設(shè)置為單例
2、若實(shí)例化此對象需要花費(fèi)較多時(shí)間,則將此對象設(shè)置為單例,增加效率
3、若此對象需要頻繁連接,釋放數(shù)據(jù)庫,文件的連接,則將此對象設(shè)置為單例
4、有狀態(tài)的工具類(如線程池類,此類要方便對池中的線程進(jìn)行控制)
餓漢模式
public class Test
{
private static final Test test = new Test();
private Test()
{
}
public static Test getInstance()
{
return test;
}
}
代碼中將Test的構(gòu)造函數(shù)設(shè)為private,則能夠保證其他對象不能通過默認(rèn)構(gòu)造函數(shù)初始化一個(gè)Test實(shí)例,保證單例。而getInstance()方法設(shè)置為靜態(tài)方法,即類方法,則其他對象可以直接調(diào)動此類方法獲得Test實(shí)例。從代碼中我們能發(fā)現(xiàn),懶漢模式的單例中,Test實(shí)例在編譯時(shí)就生成。其優(yōu)點(diǎn)是,在程序運(yùn)行時(shí)不用再對其進(jìn)行實(shí)例化,它已經(jīng)被初始化好保存在靜態(tài)內(nèi)存之中,因此效率高;但同時(shí)它就面臨一個(gè)缺點(diǎn),若有這樣的一種情況,程序調(diào)用中沒用到Test實(shí)例,但Test實(shí)例會一直存在內(nèi)存中,占用資源。
懶漢模式
從字面意義上我們就能體會到,懶漢模式就是在程序真正用到Test實(shí)例的時(shí)候再去初始化它。
public class Test
{
private static Test test = null;
private Test()
{
}
public Test getInstance()
{
if(null == test)
{
test = new Test();
}
return test;
}
}
同餓漢模式一樣,將構(gòu)造函數(shù)設(shè)為private防止其他對象調(diào)用默認(rèn)構(gòu)造函數(shù)創(chuàng)建Test實(shí)例。但與餓漢模式不同的是,test變量設(shè)置為在一開始并未對其初始化,而是在調(diào)用getInstance()后進(jìn)行判斷,若test為空才對其進(jìn)行初始化,保證了test實(shí)例初始化時(shí)間在實(shí)際調(diào)用的時(shí)候才發(fā)生。
但懶漢模式面臨一個(gè)問題,在多線程情況下,若線程A和線程B幾乎同時(shí)調(diào)用test,此時(shí)A剛好在執(zhí)行new Test()的時(shí)候B正好運(yùn)行到判斷null == test這條語句,此時(shí)就會產(chǎn)生出兩個(gè)test實(shí)例,因此多線程情況下,并不能保證單例。
為了改進(jìn)懶漢模式的線程不安全問題,引入了Synchronizend關(guān)鍵字,保證線程的安全對代碼進(jìn)行了如下改正
public class Test
{
private static Test test = null;
private Test()
{
}
public Test getInstance()
{
if(null == test)
{
Synchronized(test.class)
{
if(null == test)
{
test = new Test();
}
}
}
return test;
}
}
此時(shí)為A B兩線程同時(shí)調(diào)用Test實(shí)例的時(shí)候,若A先進(jìn)入test為空的判斷則會對代碼進(jìn)行加鎖,只有在Test實(shí)例化成功后再釋放鎖,此時(shí)B再運(yùn)行。那為啥此段代碼中加上兩重判斷呢?若存在這樣的情況,當(dāng)A釋放鎖后B獲得鎖,若此時(shí)不加以判斷,B仍然認(rèn)為test為空(因?yàn)榕锌照Z句在鎖前面)此時(shí)它仍然會繼續(xù)創(chuàng)建一個(gè)test實(shí)例,但此時(shí)若再加上一個(gè)判空,則能保證當(dāng)B獲得鎖后Test不會再被實(shí)例化。