單例模式,多例模式及其應用
阿新 • • 發佈:2019-01-30
單例模式:
當整個應用只允許出現一個類例項時,我們經常用到單例模式。比如工具類,國際化服務提供類等等
單例模式分為兩種,一種餓漢式:在類載入的時候即完成物件建立,保證始終只有一個物件存在
一種懶漢式,在需要時建立物件,需要注意執行緒安全
以上為什麼要判斷兩次s==null呢(雙重檢查機制)?
- 執行緒 1 進入
getInstance()
方法。 - 由於
instance
為null
,執行緒 1 在 //1 處進入synchronized
塊。 - 執行緒 1 被執行緒 2 預佔。
- 執行緒 2 進入
getInstance()
方法。 - 由於
instance
仍舊為null
,執行緒 2 試圖獲取 //1 處的鎖。然而,由於執行緒 1 持有該鎖,執行緒 2 在 //1 處阻塞。 - 執行緒 2 被執行緒 1 預佔。
- 執行緒 1 執行,由於在 //2 處例項仍舊為
null
,執行緒 1 還建立一個Singleton
物件並將其引用賦值給instance
- 執行緒 1 退出
synchronized
塊並從getInstance()
方法返回例項。 - 執行緒 1 被執行緒 2 預佔。
- 執行緒 2 獲取 //1 處的鎖並檢查
instance
是否為null
。 - 由於
instance
是非null
的,並沒有建立第二個Singleton
物件,由執行緒 1 建立的物件被返回。
請注意,看起來好像雙重檢查鎖定背後的理論是完美的。不幸地是,現實完全不同。雙重檢查鎖定的問題是:並不能保證它會在單處理器或多處理器計算機上順利執行。
雙重檢查鎖定失敗的問題並不歸咎於 JVM 中的實現 bug,而是歸咎於 Java 平臺記憶體模型。記憶體模型允許所謂的“無序寫入”,這也是這些習語失敗的一個主要原因。
鑑於此,筆者個人偏向於採用餓漢式建立方式。
多例模式:
多例模式應用場景舉例:
多併發請求環境下,系統需要為每個客戶端的獨立請求提供單獨服務的資源,但是系統總的開銷是有限的,系統在併發量很大時也不可能為所有的併發請求同時提供相應的資源,否則不但系統資源消耗量大而且非常耗時。這時就可以考慮使用池的概念,也即是一種多例模式的實現。具體的應用場景,比如資料庫連線池、EJB無狀態會話Bean的例項池
程式碼實現上一般是提供一個容器類,也即是容納資源物件的池,物件池的一些屬性可以通過配置檔案來配置,比如資料庫連線池中容納的Connection型別的物件數目的上限和下限、閒置連線超時時間等;然後每當應用程式請求資料庫連線時,先判斷池中有無空閒的連線,如有,即返回這個物件,如沒有,則新建一個連線物件,並放入連線池中進行管理