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極客思維

微信掃一掃,關注公眾號