摘要:在設計模式一書中,將單例模式稱作單件模式。通過關鍵字,來保證不會同時有兩個線程進入該方法的實例對象改善多線程問題為了符合大多數程序,很明顯地,我們需要確保單例模式能在多線程的情況下正常工作。
在《Head First 設計模式》一書中,將單例模式稱作單件模式。這里為了適應大環境,把它稱之為大家更熟悉的單例模式。
一、了解單例模式1.1 什么是單例模式
單例模式確保一個類只有一個實例,并提供一個安全訪問點。
我們把某個類設計成自己管理的一個多帶帶實例,同時也避免其他類再自行產生實例。想要獲取單例實例,通過單例類是唯一的途徑。單例類提供對這個實例的全局訪問點:當你需要實例時,向類查詢,它會返回單個實例。
1.2 單例模式 UML 圖解
1.3 單例模式應用場景
需要頻繁實例化然后銷毀的對象。
創建對象時耗時過多或者耗資源過多,但又經常用到的對象。比如線程池、緩存、日志對象等。
有狀態的工具類對象。
頻繁訪問數據庫或文件的對象。
以及要求只有一個對象的場景。
二、單例模式具體應用2.1 經典的單例模式實現
采用經典單例模式實現代碼有一個特點:如果我們不需要這個實例 (調用 getInstance() 方法),它就永遠不會產生。因此這種方式也被稱為“延遲實例化”(lazy instantiaze)。也被大家稱為“懶漢式”。
單例類 Singleton
package com.jas.singleton; public class Singleton { // 用靜態變量來記錄 Singleton 類的唯一實例 private static Singleton uniqueInstance; /** * 把構造器聲明為私有的,只有自己 Singleton 內部才可以調用構造器 */ private Singleton(){} /** * getInstance() 方法來實例化對象 * * @return Singleton 的實例對象 */ public static Singleton getInstance(){ if(uniqueInstance == null){ uniqueInstance = new Singleton(); } return uniqueInstance; } }
測試類
package com.jas.singleton; public class SingletonTest { public static void main(String[] args) { Singleton singleton1 = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); System.out.println(singleton1 == singleton2); } } /** * 輸出 * true */
這雖然是經典的單例模式,但是這樣做卻存在著一個嚴重的問題:當多個線程同時訪問 getInstance() 方法時,會產生線程安全問題,可能導致產生的實例可能會有多個,這樣就違反了單例的原則。
2.2 處理多線程
存在線程安全問題,我們的第一反應可能是加同步鎖。就像下面這樣,這樣做是可以解決線程安全問題,但是卻降低了性能。因為只有在第一次執行該方法的時候,才真正需要同步。之后再調用此方法,同步反而會成為一種累贅。
/** * 通過 synchronized 關鍵字,來保證不會同時有兩個線程進入該方法 * * @return Singleton 的實例對象 */ public synchronized static Singleton getInstance(){ if(uniqueInstance == null){ uniqueInstance = new Singleton(); } return uniqueInstance; }
2.3 改善多線程問題
為了符合大多數 Java 程序,很明顯地,我們需要確保單例模式能在多線程的情況下正常工作。但是同步的做法會擊垮其性能,所以提供以下幾種方法來解決問題。
(1) 直接同步
直接同步雖然會降低性能,但是如果你的程序可以承受 getInstance() 造成的額外代價,同步確實是一種既簡單又有效的方法。但是你必須知道,同步一個方法,可能會使程序的執行效率下降幾十倍。因此,如果你需要頻繁使用單例對象,那么你就要重新考慮設計了。
(2) “急切”創建實例
如果應用程序總是創建并使用單例創建的對象,或者在創建和運行時方面的負擔不太嚴重,你可以急切 (early) 創建此對象。這種方式也被大家稱為“惡漢式”。就像下面這樣
package com.jas.singleton; public class Singleton { //在靜態初始化器中創建對象,用來保證線程安全 private static Singleton uniqueInstance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return uniqueInstance; } }
利用上面這種做法,我們依賴 JVM 在加載這個類時馬上創建此唯一的實例。JVM 保證在任何時候任何線程訪問 getInstance() 方法之前,一定會先創建此實例。這樣一來就可以解決多線程之間的安全問題。
(3)雙重檢驗加鎖
利用雙重檢驗加鎖 (double-checked locking),首先檢查實例是否已經被創建了,如果未創建,“才”開始同步。這樣一來,只有第一次會同步,這樣做正是我們想要的。
package com.jas.singleton; public class Singleton { //volatile 關鍵字用來保證內存可見性,使多線程正確處理 uniqueInstance 對象 private static volatile Singleton uniqueInstance; private Singleton(){} public static Singleton getInstance(){ //使用這種方式,只有第一次才會徹底訪問并執這里的代碼 if(uniqueInstance == null){ //檢查實例,如果不存在進入同步區 synchronized (Singleton.class){ if(uniqueInstance == null){ //進入同步區后,再檢查一次。如果為 null,才開始創建實例 uniqueInstance = new Singleton(); } } } return uniqueInstance; } }
如果你性能是你關心的重點,那么這種方式會幫你大大減少訪問 getInstance() 時的時間消耗。需要在注意的是:這種雙重檢驗加鎖的方式并不適用于 1.4 及之前更早的版本。
三、單例模式總結3.1 優缺點總結
優點
實例控制:單例模式會阻止其他對象實例化其自己的單例對象的副本,從而確保所有對象都訪問唯一實例。
靈活性:因為類控制了實例化過程,所以類可以靈活更改實例化過程。
缺點
開銷:雖然數量很少,但如果每次對象請求引用時都要檢查是否存在類的實例。
可能的開發混淆:使用單例對象(尤其在類庫中定義的對象)時,開發人員必須記住自己不能使用 new 關鍵字實例化對象。因為可能無法訪問庫源代碼,因此應用程序開發人員可能會意外發現自己無法直接實例化此類。
對象生存期:不能解決刪除單個對象的問題。在提供內存管理的語言,只有單例類能夠導致實例被取消分配,因為它包含對該實例的私有引用。
3.2 部分知識總結
單例模式確保程序中一個類最多只有一個實例。單例模式也提供訪問這個實例的全局點。
如果你使用多個類加載器,可能導致單例模式失效,從而產生多個實例。
確定性能和資源上的限制,我們應當選擇合適的方案來實現單例模式。
參考資料《Head First 設計模式》
https://www.cnblogs.com/tufujie/p/5614682.html
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77363.html
摘要:總之,選擇單例模式就是為了避免不一致狀態,避免政出多頭。二餓漢式單例餓漢式單例類在類初始化時,已經自行實例化靜態工廠方法餓漢式在類創建的同時就已經創建好一個靜態的對象供系統使用,以后不再改變,所以天生是線程安全的。 概念: Java中單例模式是一種常見的設計模式,單例模式的寫法有好幾種,這里主要介紹兩種:懶漢式單例、餓漢式單例。 單例模式有以下特點: 1、單例類只能有一個實例。 ...
摘要:原文博客地址單例模式系統中被唯一使用,一個類只有一個實例。中的單例模式利用閉包實現了私有變量兩者是否相等弱類型,沒有私有方法,使用者還是可以直接一個,也會有方法分割線不是單例最簡單的單例模式,就是對象。 原文博客地址:https://finget.github.io/2018/11/06/single/ 單例模式 系統中被唯一使用,一個類只有一個實例。實現方法一般是先判斷實例是否存在,...
摘要:最近開展了三次設計模式的公開課,現在來總結一下設計模式在中的應用,這是第一篇創建型模式之單例模式。不過因為不支持多線程所以不需要考慮這個問題了。 最近開展了三次設計模式的公開課,現在來總結一下設計模式在PHP中的應用,這是第一篇創建型模式之單例模式。 一、設計模式簡介 首先我們來認識一下什么是設計模式: 設計模式是一套被反復使用、容易被他人理解的、可靠的代碼設計經驗的總結。 設計模式不...
摘要:一懶漢式線程不安全懶漢式線程不安全私有構造方法只允許在內部進行實例的創建創建實例二懶漢式線程安全懶漢式線程安全私有構造方法只允許在內部進行實例的創建創建實例線程安全三餓漢式線程安全餓漢式私有構造方法只允許在內部進行實例的創建靜態初始化由保證 一、懶漢式(線程不安全) package com.java.singleton; //懶漢式 線程不安全 public class LazySi...
摘要:下面我們來看看看中的單例模式,中使用的是單例注冊表的特殊方式實現的單例模式,所以說模式是死的,需要靈活得運用。 本文循序漸進介紹單例模式的幾種實現方式,以及Jdk中使用到單例模式的例子,以及sring框架中使用到的單例模式例子。 餓漢式 package signgleton; /** * 單例模式簡單的實現 */ public class Singleton { priv...
閱讀 831·2021-09-07 09:58
閱讀 2686·2021-08-31 09:42
閱讀 2862·2019-08-30 14:18
閱讀 3091·2019-08-30 14:08
閱讀 1837·2019-08-30 12:57
閱讀 2762·2019-08-26 13:31
閱讀 1304·2019-08-26 11:58
閱讀 1059·2019-08-23 18:06