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