所謂的單例模式就是保證某個類在程序中只有一個對象
一、如何控制只產(chǎn)生一個對象?
1.構(gòu)造方法私有化(保證對象的產(chǎn)生個數(shù))
? ? ? ? 創(chuàng)建類的對象,要通過構(gòu)造方法產(chǎn)生對象
? ? ? ?構(gòu)造方法若是public權(quán)限,對于類的外部,可以隨意創(chuàng)建對象,無法控制對象個數(shù)
? ? ? 構(gòu)造方法私有化,這樣類的外部就徹底無法產(chǎn)生對象,一個對象都沒有。
2.單例類的內(nèi)部提供這個唯一的對象(static)
? ? ? ? 構(gòu)造方法私有化后,對于類的外部而言就一個對象都沒有了。因此要在這個類的內(nèi)部構(gòu)造出這個唯一的對象,只調(diào)用一次構(gòu)造方法即可(這個單例對象不能是類的成員屬性,因為成員變量必須通過對象來訪問,但是類的外部根本無法產(chǎn)生對象,(矛盾),因此這個對象必須使用static關(guān)鍵字修飾,靜態(tài)變量,不依賴類的對象)
3.單例類提供返回這個唯一對象的靜態(tài)方法供外部使用
二、餓漢式單例
餓漢式單例模式是天然的線程安全的。類加載時就創(chuàng)建了這個唯一的對象!?。?/span>
/**
* 餓漢式單例(類加載就產(chǎn)生這個唯一的對象,也不管外部是否調(diào)用該對象)。饑不擇食,這個類一加載就把惟一的這個對象產(chǎn)生了,
* 我也不管外部到底用不用這個對象,只要這個類加載到JVM,唯一對象就會產(chǎn)生
**/
public class SingleTon {
// 惟一的這一個對象
private static SingleTon singleTon = new SingleTon();
private SingleTon() {}
// 調(diào)用此方法時,singleTon對象已經(jīng)產(chǎn)生過了,多線程場景下取回的是同一個單例對象
public static SingleTon getSingleton() {
return singleTon;
}
}
三、懶漢式單例
懶漢式單例:只有第一次調(diào)用getSingleTon(),表示外部需要獲取這個單例對象時才產(chǎn)生對象
public class LazySingleTon {
private static LazySingleTon singleTon ;
private LazySingleTon(){}
public LazySingleTon getSingleTon(){
if (singleTon == null){
singleTon = new LazySingleTon();
}
return singleTon;
}
}
多線程場景下會產(chǎn)生線程安全問題(不能確保只有一個對象產(chǎn)生)?
在這個場景下,三個線程并發(fā)調(diào)用get方法,此時三個?線程看到的singleTon 都為null,因此,每個線程都創(chuàng)建了一個對象!!
四、解決懶漢式單例的線程安全問題
1.靜態(tài)方法上加鎖
public synchronized static LazySingleTon getSingleTon(){
if (singleTon == null){
singleTon = new LazySingleTon();
}
return singleTon;
}
在方法上上鎖,表示同一時間只有一個線程能進入此方法(其他線程想要進入此方法都等待獲取鎖成功的線程釋放鎖)。此時,getSingleTon()的內(nèi)部都是單線程操作(鎖的粒度太粗)。
2.double-check(雙重加鎖)
private volatile static LazySingleTon singleTon ;
private LazySingleTon(){}
public static LazySingleTon getSingleTon(){
if (singleTon == null){
synchronized (LazySingleTon.class){
if (singleTon == null){
singleTon = new LazySingleTon();
}
}
}
return singleTon;
}
volatile的作用:內(nèi)存屏障,可見性
此時有t1,t2,t3三個線程,t1首先獲取到了鎖,開始執(zhí)行new操作,雖然還沒完全結(jié)束,但此時的singleTon != null,對于剛開始執(zhí)行代碼的t2,t3來說,它們看到singleTon != null 直接返回了,但是返回后的單例對象是一個尚未完全初始化的對象
此時采用volatile關(guān)鍵字修飾單例對象,new操作有著一堵墻,其它線程要能執(zhí)行到return語句,JVM一定保證了new操作完全結(jié)束了,之后才會執(zhí)行return語句。
?double-check:防止其他線程恢復(fù)執(zhí)行后多次創(chuàng)建單例對象
當(dāng)t1先進入同步代碼塊后,t2,t3卡在獲取所得位置,
t1產(chǎn)生對象后釋放鎖,
t2,t3還是從獲取鎖的位置繼續(xù)執(zhí)行,在他們的工作內(nèi)存中,singleTon == null?文章來源:http://www.zghlxwxcb.cn/news/detail-704533.html
t2,t3就會再次new對象。文章來源地址http://www.zghlxwxcb.cn/news/detail-704533.html
到了這里,關(guān)于單例模式(餓漢式單例 VS 懶漢式單例)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!