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