單例就是隻有一個例子,只有一個物件,不允許別人再建立物件。
餓漢式(初始化即建立物件)
class Single{ private static Single s = new Single(); private Single(){} public static Single getInstance(){ return s; } }
懶漢式(方法被呼叫時,才建立物件,也叫做物件的延時載入)
class Single{ private static Single s = null; private Single(){} public static Single getInstance(){//當多人同時呼叫此方法時有可能出狀況 if(s == null) //語句① s = new Single();//語句② return s; } }
懶漢式看似省空間,卻有可能在多執行緒時出問題。
舉個只有兩個執行緒的例子:執行緒A被單核CPU執行到①,單核CPU切入執行緒B去執行①,仍然會通過判斷,此時A,B都會執行語句②。
改進後的安全懶漢式(低效,在方法上增加了執行緒鎖):
class Single{ private static Single s = null; private Single(){} public synchronized static Single getInstance(){ if(s == null) //語句① s = new Single();//語句② return s; } }
tips:
執行緒鎖就是synchronized後邊的引數,漢語版的API中作者稱之為物件監視器。執行緒鎖有兩個狀態,一個鎖住一個開啟,開啟的時候執行緒就能進去,關閉的時候,執行緒就會在門前等待,直到鎖開啟才會進去。
synchronized相當於一個標示符表示它所跟隨的大括號內的內容是同步程式碼塊,執行這部分程式碼塊就要判斷執行緒鎖的狀態。
再次改進後最終的懶漢式(在方法內部增加執行緒鎖)
class Single{ private static Single s = null; private Single(){} public static Single getInstance(){ if(s == null){ //語句① synchronized (Single.class){ //語句② 這樣就會最多判斷兩次執行緒鎖 if(s == null) //語句③ s = new Single();//語句④ } } return s; } }
解析一下:執行緒A執行語句①通過,執行語句②通過,此時CPU切入執行緒B執行到語句①通過,執行到語句②未通過,
然後CPU切入執行緒A繼續執行,通過語句③和④並解除執行緒鎖,CPU再次切入執行緒B,此時會通過語句②,執行語句③,
如果沒有語句③又悲劇了。。。
總結,既然有這麼一個單例類,肯定你是要用它的,你要用它一定會開闢記憶體存放它的物件,
懶漢實在是浪費時間又沒什麼實際意義,所以建議選擇餓漢式的單例模式。