設計模式之單例模式

2022-09-06 18:33:09 字數 4115 閱讀 1047

目錄:

一、什麼是單例模式

單例模式顧名思義就是只存在乙個例項,也就是系統**中只需要乙個物件的例項應用到全域性**中,有點類似全域性變數。例如,在系統執行時,系統需要讀取配置檔案中的引數,在設計系統的時候讀取配置檔案的類往往設計成單例類。因為系統從啟動執行到結束,只需要讀取一次配置檔案,這個讀取配置檔案全部由該類負責讀取,在全域性**中只需要使用該類即可。這樣不僅簡化了配置檔案的管理,也避免了**讀取配置檔案資料的不一致性。

單例模式的特點:

1、該類的構造方法宣告為private,這樣其他類無法初始花該類,只能通過該類的public方法獲取該類的物件。

2、裡面有個私有的物件成員,該成員物件是類本身,用於public方法返回該類的例項。

3、該類中提供乙個public的靜態方法,返回該類的私有成員物件。

二、單例的應用場景

舉乙個小例子,在我們的windows桌面上,我們開啟了乙個**站,當我們試圖再次開啟乙個新的**站時,windows系統並不會為你彈出乙個新的**站視窗。,也就是說在整個系統執行的過程中,系統只維護乙個**站的例項。這就是乙個典型的單例模式運用。

繼續說**站,我們在實際使用中並不存在需要同時開啟兩個**站視窗的必要性。假如我每次建立**站時都需要消耗大量的資源,而每個**站之間資源是共享的,那麼在沒有必要多次重複建立該例項的情況下,建立了多個例項,這樣做就會給系統造成不必要的負擔,造成資源浪費。

再舉乙個例子,**的計數器,一般也是採用單例模式實現,如果你存在多個計數器,每乙個使用者的訪問都重新整理計數器的值,這樣的話你的實計數的值是難以同步的。但是如果採用單例模式實現就不會存在這樣的問題,而且還可以避免執行緒安全問題。同樣多執行緒的執行緒池的設計一般也是採用單例模式,這是由於執行緒池需要方便對池中的執行緒進行控制

從上述的例子中我們可以總結出適合使用單例模式的場景和優缺點:  

適用場景:

1.需要生成唯一序列的環境

2.需要頻繁例項化然後銷毀的物件。

3.建立物件時耗時過多或者耗資源過多,但又經常用到的物件。 

4.方便資源相互通訊的環境

三、單例模式的優缺點

優點

1、在記憶體中只有乙個物件,節省記憶體空間;

2、避免頻繁的建立銷毀物件,可以提高效能;

3、避免對共享資源的多重占用,簡化訪問;

4、為整個系統提供乙個全域性訪問點。

缺點

1、不適用於變化頻繁的物件;

2、濫用單例將帶來一些負面問題,如為了節省資源將資料庫連線池物件設計為的單例類,可能會導致共享連線池物件的程式過多而出現連線池溢位;

3、如果例項化的物件長時間不被利用,系統會認為該物件是垃圾而被**,這可能會導致物件狀態的丟失;

四、單例模式的實現

1、餓漢式

public

class

mgr;

//公共方法,返回自己的例項化成員

public

static

mgr getmgr()

}

備註:該單例實現方法簡單明瞭,推薦使用。該類被jvm加到記憶體的時候,只會載入一次,並且只例項化乙個單例,優點是具有執行緒安全性,缺點是:不用他也在記憶體中例項化,浪費記憶體。所以提出了懶散式實現方式。

2、懶漢式

public

class

mgr;

//懶漢的特點,提供公共靜態方法,如果該成員物件為空,例項化並返回

public

static

mgr getmgr()

return

mgr;}}

備註:(不推薦用)這種實現方法只有程式在呼叫該類的getmgr方法才例項話物件並返回,特點就是呼叫的時候再例項化並返回,延遲載入的被動形式。但是該實現方法不是執行緒安全的,因為當同時有有兩個執行緒執行到if(mgr==null)語句的時候,由於某些原因其中乙個執行緒先一步執行下一句,例項化了物件並返回;兩乙個執行緒再例項化物件在返回,這兩個執行緒返回的物件不是同乙個物件(這難道還是單例嗎!),所以該實現方法的缺點也很明顯。那為了避免執行緒不安全問題,在懶漢寫法上提出加鎖的實現方式。

3、給公共方法加鎖

public

class

mgr;

//給公共方法加鎖,只有乙個執行緒第一次獲得鎖例項化物件並返回

public

static

syncnronized mgr getmgr()

return

mgr;}}

備註:(推薦使用)這種實現方式比較完善,既保證了懶散式的延遲載入方式,也保證了執行緒安全。缺點是在整個方法上加鎖,導致效能下降。因為只有第一次獲得鎖的執行緒例項化物件並返回,以後的執行緒獲得鎖的時候執行 if(mgr == null)語句的時候,由於mgr已經例項化了不為空,直接跳過返回例項。整個過程要競爭鎖,不能併發執行導致效能下降。那為優化效能下降問題,那我只在mgr = new mgr()上加鎖,保證鎖粒度最小化的同時保證單例例項化。

4、給例項化加鎖

public

class

mgr;

//公共方法,在例項化語句塊加鎖,保證單例

public

static

mgr getmgr()

}return

mgr;

}}

備註:(不推薦使用)該實現方法雖然相較方法3效能有所提公升,但並不能保證執行緒安全。因為當兩個執行緒同時執行if(mgr == null)語句時,其中執行緒1獲取鎖,例項化物件並返回,執行緒2在獲得鎖又在例項化物件並返回。執行緒1和執行緒2獲取的物件並不是同乙個。所以在此基礎上提出了雙重判斷方式。

5、雙重判斷加鎖

public

class

mgr;

//公共方法提供雙重判斷並在例項化**塊加鎖

public

static

mgr getmgr() }}

return

mgr;

}}

備註:(推薦使用)相較於方法4,該方法雙重判定,如果多執行緒同時執行到第一次判斷語句位置,其中乙個執行緒獲得鎖,由於是第一次該物件成員為空,例項化後並返回。其後其它執行緒呼叫公共方法的時候,由於例項化了,在第一次判斷自接返回例項,不在產生鎖競爭。大大提高了效率,保證了執行緒的安全性,也保證了延遲載入的特性。

6、靜態內部類

public

class

mgr;

//定義靜態內部類

private

static

class

mgrholder

//公共方法直接返回靜態內部類的例項物件

public

static

mgr getmgr()

}

備註:(可使用)該實現方法通過jvm來保證執行緒安全性,靜態內部類mgrholder來new乙個mgr物件,jvm只會載入一次mgr類(靜態內部類不會載入),當類呼叫getmgr方法的時候,也只會呼叫一次,公共方法呼叫靜態內部類,獲取乙個物件(也是實現懶載入)。所以也是執行緒安全的。

7、列舉類單例模式

public

enum

mgr //

業務方法

}

備註:(推薦使用)jdk1.5之後才能正常達到單例效果,參考來自《effective j**a》。注意列舉類的列舉變數必須寫在第一行,後面實現業務**。呼叫方式是:mgr.mgr.function_name();具備列舉型別的特點,有點是:執行緒同步,防止反序列化。

五、總結

通過上面幾種單例模式的實現方式的列舉,但是在實際應用中其中的2,3,4三種方式並不適用,列出來只是讓讀者更好的理解方式5的由來,起到拋磚引玉的作用,更好的理解單例模式。總之常用的四種,懶漢,雙重校驗鎖,靜態內部類,列舉單例。

餓漢:類載入的時候就建立例項,所以是執行緒安全的,但不能延遲載入。

雙重校驗鎖:執行緒安全,效率較高,延遲載入。

靜態內部類:實現起來比較麻煩,在不同的編譯器上會出現不可預知的錯誤。

列舉單例:很好,不僅避免了多執行緒同步問題,而且能反正反序列化重新建立物件,但是不能延遲載入,用的人少。

設計模式之單例模式

前一段時間買了一本秦小波寫的 設計模式之禪 網上對這書的評價很高。現在還沒有看很多,但是有些地方頗有感觸,也並不是所有的地方都能看懂,但是會慢慢研究的。自己對於設計模式的感覺就是乙個字 牛!感覺會23種設計模式並且會熟練運用的人,真的就是大師級的牛人了,設計模式是乙個專案主管或者架構師一定要會的東西...

設計模式之單例模式

package com.xie.singleton public class singleton 提供乙個共有的靜態的入口方法 public static singleton getinstance 懶漢式 延遲載入 提供乙個私有的靜態的成員變數,但不做初始化 private static sing...

設計模式之 單例模式

單例模式 singleton 保證乙個類僅有乙個例項,並提供乙個訪問它的全域性訪問點。單例模式 單件模式 使用方法返回唯一的例項 public class singleton private static singleton instance public static singleton geti...