本文從單例模式的一般實現方式開始說起,逐步深入到雙重加鎖實現。
首先介紹一下最簡單的單例模式——餓漢模式,這種方式在單例類被載入的時候例項化。**實現如下:
1 public class singleton
7 8 private singleton()
10 11 public static singleton getinstance()
14 }
餓漢模式的缺點在於,如果單例物件的建立過程比較耗時,那麼應用程式的啟動將會比較慢。
為了克服餓漢模式的缺點,將單例物件的建立過程延後到第一次使用單例物件時,這種實現方式被稱為懶漢模式。**實現如下:
1 public class singleton
6
7 public static singleton getinstance()
11
12 return instance;
13 }
14 }
需要注意的是這種實現方式是執行緒不安全的。假設在單例類被例項化之前,有兩個執行緒同時在獲取單例物件,執行緒1在執行完第8行 if (instance = = null) 後,執行緒排程機制將 cpu 資源分配給執行緒2,此時執行緒2在執行第8行 if (instance == null) 時也發現單例類還沒有被例項化,這樣就會導致單例類被例項化兩次。為了防止這種情況發生,需要對 getinstance() 方法同步。下面看改進後的懶漢模式:
1 public class singleton
6
7 // 執行緒安全的懶漢模式
8 public synchronized static singleton getinstance()
12
13 return instance;
14 }
15 }
雙重加鎖(double check)
第2種實現方式中,每次獲取單例物件時都會加鎖,這樣就會帶來效能損失。雙重加鎖實現本質也是一種懶漢模式,相比第2種實現方式將會有較大的效能提公升。**實現如下:
1 public class singleton
6
7 public static singleton getinstance()
13 }
14 }
15
16 return instance;
17 }
18 }
就算在單例類被例項化時有多個執行緒同時通過了第8行** if (instance == null) 的判斷,但同一時間只有乙個執行緒獲得鎖後進入臨界區。通過第8行判斷的每個執行緒會依次獲得鎖進入臨界區,所以進入臨界區後還要再判斷一次單例類是否已被其它執行緒例項化,以避免多次例項化。由於雙重加鎖實現僅在例項化單例類時需要加鎖,所以相較於第2種實現方式會帶來效能上的提公升。另外需要注意的是雙重加鎖要對 instance 域加上 volatile 修飾符。由於 synchronized 並不是對 instance 例項進行加鎖(因為現在還並沒有例項),所以執行緒在執行完第11行修改 instance 的值後,應該將修改後的 instance 立即寫入主存(main memory),而不是暫時存在暫存器或者高速緩衝區(caches)中,以保證新的值對其它執行緒可見。
補充:第9行可以鎖住任何乙個物件,要進入臨界區必須獲得這個物件的鎖。由於並不知道其它物件的鎖的用途,所以這裡最好的方式是對 singleton.class 進行加鎖。
研磨設計模式 單例模式 雙重檢查加鎖
雙重檢查加鎖 雙重檢查加鎖 的方式可以既實現執行緒安全,又能夠使效能不受到很大的影響。那麼什麼是 雙重檢查加鎖 機制呢?所謂雙重檢查加鎖機制,指的是 並不是每次進入getinstance方法都需要同步,而是先不同步,進入方法過後,先檢查例項是否存在,如果不存在才進入下面的同步塊,這是第一重檢查。進入...
深入理解設計模式(一) 單例模式
本文首先概述了單例模式,揭示了單例模式的應用場景和優缺點,最後我們給出了單例模式的幾種實現方式及注意事項。單例模式是一種常用的軟體設計模式,其定義是單例物件的類只能允許乙個例項存在。許多時候整個系統只需要擁有乙個的全域性物件,這樣有利於我們協調系統整體的行為。比如在某個伺服器程式中,該伺服器的配置資...
設計模式之單例模式(懶漢 餓漢 雙重檢查加鎖)
目錄 經典的實現方式 多執行緒問題 解決方案 懶漢 餓漢雙重檢查加鎖 在程式中,有些物件是只需要乙個的,比如執行緒池 快取 日誌物件等。這個時候,單例模式閃亮登場,它確保了乙個類只有乙個例項,並提供全域性訪問點。下面是比較經典的實現方式,將構造器宣告為私有的,同時提供static修飾的getinst...