設計模式 一 五種單例模式剖析

2021-10-23 15:22:50 字數 4377 閱讀 8915

本篇文章將本人在學習設計模式之初以及後續產生的問題進行彙總並逐一解答。

如:1.雙重檢查加鎖為什麼可以提高效率。

2.懶漢,餓漢模式中例項物件的引用都具有static,二者的具體區別。

**保證乙個類只有乙個例項,並提供它的乙個全域性訪問點。**
要控制乙個類只能有乙個例項,就要把建立例項的許可權收回,讓類自己負責建立例項的工作,然後由這個類提供對外的可以訪問該物件的方法,便實現了單例模式。

所謂餓漢式,顧名思義,就是在建立物件時比較著急,因此在裝載類的時候直接建立乙個靜態例項。
1)構造器私有

private

hungryman()

2)建立乙個hungryman型別的常量hungry_man

private

static hungryman hungry_man =

newhungryman()

;

3)提供對外訪問物件的方法

public

static hungryman getinstance()

將三步合併後,就是如下所示:

class

hungryman

//2.建立乙個hungryman型別的常量hungry_man

private

static hungryman hungry_man =

newhungryman()

;//3.提供對外訪問物件的方法

public

static hungryman getinstance()

}

餓漢單例模式是執行緒安全的,因為虛擬機器只會裝載類一次,後續無論是哪個執行緒呼叫,return的都是同乙個靜態變數,不會出現併發。

1)構造器私有

private

lazyman()

2)建立乙個lazyman物件的引用(因為是懶漢,所以不急著建立,等需要的時候在建立)

private

static lazyman lazyman = null;

3)提供對外訪問物件的方法

public

static lazyman getinstance()

對以上三步進行合併:

class

lazyman

//2.建立乙個lazyman物件的引用(因為是懶漢,所以不急著建立,等需要的時候在建立)

private

static lazyman lazyman = null;

//3.提供對外呼叫的方法

public

static lazyman getinstance()

**此處應注意:

成員變數lazyman由於要在靜態方法getinstance中使用,所以這個屬性必須被迫加上static關鍵字,因為靜態成員只能呼叫靜態成員,也就是說,懶漢模式並沒有使用static的特性

相反,餓漢單例模式下static修飾的例項變數hungry_man體現了static的特性

**

分析:懶漢單例是執行緒不安全的。 當某一線程執行到if語句準備建立物件時,另一線程可能也會進入,所以需要進行改進。

2.1延時載入的思想:

單例模式中的懶漢模式體現了延遲的思想,那麼,什麼事延遲載入呢?

延時載入是典型的以時間換空間 ,通俗的說,就是一開始不要載入資料或資源,等到不得不載入的時候再去進行載入,這麼做可以盡可能地節約資源。

//這裡就體現了延時載入的思想,在建立前先進行判斷,若果沒有例項,在進行建立。

if(lazyman == null)

lazyman =

newlazyman()

;return lazyman;

public

static lazyman getinstance()

上面說的第一種懶漢模式是執行緒不安全的.

為了設計出更好的程式,立刻進行嘗試!

嘗試一:

假設有兩個執行緒a和b同時呼叫getinstance方法,當執行緒a執行完第二行**準備執行第三行時,而執行緒b剛執行完第一行,正在進行判斷,程式繼續進行,

1.由於執行緒b執行較快,一下就判斷出lazyman == null,為true

2.而此時執行緒a正在建立例項,new lazyman();

3.但執行緒b也已經判斷結束,也進行到第三行,

4.這是就產生了問題,建立了兩個例項物件

這是要想解決執行緒不安全的問題,使用synchronized關鍵字即可

public static synchronized lazyman getinstance()

但是這樣以來,會使得每個呼叫getinstance方法的執行緒都需要在方法外徘徊,也就是說即使lazyman 不為空,可以直接return,但也會被拒之門外,所以大大降低了程式執行的效率。

所以第一種嘗試不可用!!!

嘗試二,因為此次嘗試是正確的,所以直接上**

在getinstance方法內部進行加鎖以及雙重判斷

class

lazyman2

//2.建立乙個lazyman物件的引用

private

static

volatile lazyman2 lazyman2 = null;

//3.提供對外呼叫的方法

/** * 雙檢鎖,又叫雙重校驗鎖,綜合了懶漢式和餓漢式兩者的優缺點整合而成。

* 看下面**實現中,特點是在synchronized關鍵字內外都加了一層 if 條件判斷,

* 這樣既保證了執行緒安全,又比直接上鎖提高了執行效率,還節省了記憶體空間。

*/public

static lazyman2 getinstance()

}return lazyman2;

}}

第一次判斷是為了排除例項已存在的情況,也就是說只有當例項不存在時,執行緒才會被拒之門外進行等待,反之,若例項存在,則無需排隊,直接return!

第二次判斷是為了避免建立更多的例項,假設有兩個執行緒在門外等待,執行緒a進去以後建立乙個例項,這時輪到執行緒b了,如果沒有第二次判斷,他也會接著建立乙個執行緒,所以必須雙重檢查。

故第一次判斷是為了提高效率,第二次判斷才是避免重複建立。

需要注意的是volatile關鍵字可能會遮蔽掉虛擬機器內部的一些**優化,因此若沒有特殊要求,也不要大量使用。

要想很簡單的實現執行緒安全,可以採用靜態初始化器的方法,他可以由jvm來保證執行緒的安全性,例如餓漢單例模式,但是這樣一來則會提前浪費一部分空間,如果現在有一種方法能夠在類初始化的時候不去載入例項物件不就行了?一種可行的方法就是採用內部類單例模式。

例項**如下:

class

staticlazyman

//1.構造器私有

private

staticlazyman()

//3.通過呼叫內部類,實現單例物件的初始化

public staticlazyman getinstance()

}

當getinstance方法第一次被呼叫的時候,他第一次讀取staticlazymaninner.slmi,導致staticlazymaninner.slmi類得到初始化,而這個類在裝載並初始化時,會初始化它的靜態域,從而建立staticlazyman的例項,由於是靜態的域,因此只會在虛擬機器裝載類的時候初始化一次,並有虛擬機器來保證他的執行緒安全性。

這個模式的優勢在於,getinstance方法並沒有被同步,並且只是執行乙個域的訪問,因此延遲初始化並沒有增加任何訪問成本。

enum singleton

}

當需要控制乙個雷=類的例項只有乙個,而且客戶只能從乙個全域性訪問點訪問它時,可以選用單例模式,這些功能恰好是單例模式要解決的問題。

23種設計模式 單例設計模式

using system using system.collections.generic using system.linq using system.text using system.threading.tasks namespace atest.23 定義共有方法 提供乙個全域性訪問點,同時...

23種設計模式 單例模式

某些情況,如 執行緒池,乙個專案中匯流排程數量以及生命週期,可能需要統一控制 如果執行緒池自身可建立多個例項,那麼就無法統一控制,此時,只要能控制線程池物件的數量為乙個,那麼就可以實現統一控制的目標 注意 現實中真正使用純的單例模式並不多 如 spring bean 通過配置來決定是否使用單例 執行...

單例模式(23種設計模式)

單例模式 餓漢模式 class singletondemo public static singletondemo gets 懶漢模式 class singletondemo1 public static singletondemo1 gets return instance 單例模式,懶漢式,執行...