23種設計模式 – 單例模式
23種設計模式 – 單例模式
1.關於單例模式的一些說明
單例模式:確保一個類最多只有一個實例,提供一個全局訪問點
注意:
-
單例類只能有一個實例
-
單例類必須自己創建自己的唯一實例
-
單例類必須給所有其他對象提供這一實例
單例模式可以分為兩種:預載入和懶載入(即餓漢式和懶漢式)
2.兩種形式詳解
1.預載入(餓漢式)
預先載入。還沒有使用該單例對象,但是該單例對象就已被載入到記憶體。
若沒有使用該單例對象,該對象就被載入到了記憶體,會造成記憶體的浪費。
2.懶載入(懶漢式)
為了避免記憶體的浪費,可以採用懶載入,即用到該單例對象的時候再創建。
3. 單例模式和執行緒安全
-
餓漢式只有一條語句return instance,可以保證執行緒安全。但會造成記憶體的浪費;
-
懶漢式不浪費記憶體,但是無法保證執行緒的安全。首先,if判斷以及其記憶體執行程式碼是非原子性的。其次,new Singleton()無法保證執行的順序性。不滿足原子性或者順序性,執行緒肯定是不安全的,不再贅述;
-
為什麼new Singleton()無法保證順序性。創建一個對象分三步:
JVM為了提高程式執行性能,會對沒有依賴關係的程式碼進行重排序,上面2和3行程式碼可能被重新排序。我們用兩個執行緒來說明執行緒是不安全的。執行緒A和執行緒B都創建對象。其中,A2和A3的重排序,將導致執行緒B在B1處判斷出instance不為空,執行緒B接下來將訪問instance引用的對象。此時,執行緒B將會訪問到一個還未初始化的對象(執行緒不安全)。
4. 懶漢式的執行緒安全解決方法
使用synchronized關鍵字。synchronized載入getInstace()函數上確實保證了執行緒的安全。
Synchronized是Java多執行緒編程中最常用的關鍵字。所有的Java 對象都有自己唯一的隱式同步鎖。該鎖只能同時被一個執行緒獲得,其他試圖獲得該鎖的執行緒都會被阻塞在對象的等待隊列中直到獲得該鎖的執行緒釋放鎖才能繼續工作。
注意:若要經常的調用getInstance()方法,不管有沒有初始化實例,都會喚醒和阻塞執行緒。為了避免執行緒的上下文切換消耗大量時間,如果對象已經實例化了,我們沒有必要再使用synchronized加鎖,直接返回對象。
把sychronized加在if(instance==null)判斷語句裡面,保證instance未實例化的時候才加鎖。
new一個對象的程式碼是無法保證順序性的,需要使用關鍵字volatile保證對象實例化過程的順序性。
即保證了懶漢式的執行緒安全。
Java極客思維
微信掃一掃,關注公眾號