1.單例模式理解:是爲了滿足一些場景,一個對象只能創建一個實例對象的場景。
流程:
1.構造方法私有化,
2.聲明對象 位靜態
3.類方法返回實例化後的對象。
2.將單例模式分爲兩類 懶漢式和餓漢式:
懶漢式:特點在定義Singleton是就new
public class Singleton{
private static Singleton singleton=new Singleton();
private Singleton(){}
public static Singleton newInstance(){
return singleton;
}
}
優點:簡單,而且保證了線程安全,不管有多少個線程訪問都只返回一個Singleton實例化對象
餓漢式單例模式:
1.普通
public class Singleton{
private static Singleton singleton;
private Singleton(){}
public static Singleton newInstance(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
}
線程不安全:if判斷確實可以保證只創建一個Singleton的實例對象。
但是在多線程併發場景下,由於沒有加鎖,多個線程可能都會進入到if中,那麼這些線程就可以進行對象實例化操作產生多個對象。
if(singleton==null){
//多個線程進入id判斷裏面,
singleton=new Singleton();
}
雙層檢驗鎖單例模式:
public class Singleton{
private static volatile Singleton singleton;
private Singleton(){}
public static Singleton newInstance(){
if(singleton==null){
//1
synchronized(Singleton.class){
//2
if(singleton==null){
singleton=new Singleton();
}
}
}
return singleton;//3
}
}
按照上面的註釋:來分析上面的代碼:
1.在調用類方法newInstance的時候所有線程都可以進入//1中
2.多個線程中只有一個線程會拿到鎖 進入同步代碼塊
3.拿到鎖後要再次判斷當前對象引用是否指向實例,因爲在這個線程之前還會有別的線程拿到鎖執行同步代碼塊。
到此流程基本結束:還有幾個問題:
1.singleton爲什麼要用volatile修飾?
首先,synchronized 保證了只有一個線程對singleton進行實例操作,但是操作的對象是引用數據類型,換句話說該操作不是原子的。那麼久可能發生指令重排。
正常情況:實例化對象的流程:
1.在堆上開闢空間
2.屬性初始化
3.將棧上空間指向堆。
正常是1——>2——>3
但是如果沒有volatile關鍵字就可能發生指令重排:
假設 1——>3——>2
如果沒有屬性的話沒有問題,
一旦有了屬性,假設現在有兩個屬性:
name=“hahaha”,
age=12;
由於3步驟先執行,singleton已經不爲空了,
當有新的線程嘗試獲取Singleton實例,到了外層判斷,直接返回。
導致的結果就是,有能屬性還沒有來的及初始化就直接返回了,或者初始化了name..
2.爲什麼要有雙重if判斷:
內部的if是保證只有一個Singleton實例化對象被創建。
外層的if實際上是爲了保證性能:
當Singleton已經被創建了,不進入內部,直接返回。
可以把這種機制:當做一個限量發售的鞋,
把多線程當做在排隊等待鞋的人。
當一個人進入後買了這雙鞋 實際上已經沒有這雙鞋了
通常做法是在門口立一個牌子,鞋已經售空 人看到這個牌子就可以直接走了
相當於外部的i f)。
如果沒有這個牌子,外面的人就不知道鞋已經賣完,還在不斷排隊進入鞋店,浪費資源