天天看點

單态設計模式(Singleton Design Pattern)

核心--在類的内部把構造器私有化,同時在内部産生對象,并通過類.靜态方法(static)傳回執行個體化對象的引用。

1 基本概念

單态模式是設計模式中最為人熟知的也是形式最簡單的。它的基本概念是一個類隻生成一個執行個體。

2 應用

Singleton的應用有很多,譬如對資料庫隻能有一個連接配接,或者對網站的連接配接數的計數器。

3 幾種形式

基本形式是使用private constructor和一個public的static方法來獲得類的執行個體。

1 2 3 4 5 6 7 8 9

public

class

Singleton {

private

static

Singleton instance =

new

Singleton();

private

Singleton(){}

public

static

Singleton getInstance() {

return

instance;

}

}

Snippet 1

constructor是private,是以如 Singleton s = new Singleton() 不再可行了。隻能通過

Singleton s = Singleton.getInstance(); 來獲得執行個體,而這個執行個體因為是static,全局共享一個,是以無論有多少個Singleton s = Singleton.getInstance(); 得到的執行個體都是同一個。

而Singleton 還有另外一種形式,采用lazy initialization:

1 2 3 4 5 6 7 8 9 10 11

public

class

Singleton {

private

static

Singleton instance =

null

;

private

Singleton(){}

public

static

Singleton getInstance() {

if

(instance ==

null

)

instance =

new

Singleton();

return

instance;

}

}

Snippet 2

Snippet 2同Snippet 1的差別在于:

Snippet 1在load class階段就建立對象了;

而Snippet 2隻有第一次要執行個體化的時候才會建立對象。這就是所謂的lazy initialization。

多線程問題

我們來看snippet 2,如果是單線程,沒問題,如果是多線程,問題就出現了,因為兩個線程可以同時進入if(instance == null) 這個判斷語句,是以有可能兩個線程建立兩個執行個體。

1 2 3 4 5 6 7 8 9 10 11

public

class

Singleton {

private

static

Singleton instance =

null

;

private

Singleton(){}

public

static

synchronized  Singleton getInstance() {

if

(instance ==

null

)

instance =

new

Singleton();

return

instance;

}

}

Snippet 3

然而Snippet 3的問題是在建立了對象之後, instance = new Singleton() 這個語句就再也不會執行了,是以對整個方法進行同步的話效率低下,這樣就有人想出了Double-checked locking的方法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public

class

Singleton {

private

static

Singleton instance =

null

;

private

Singleton(){}

public

static

Singleton getInstance() {

if

(instance ==

null

)

synchronized(Singleton.

class

){

if

(instance ==

null

)

instance =

new

Singleton();

}

return

instance;

}

}

Snippet 4

這樣就解決了問題,僅僅對

1 2

if

(instance ==

null

)

instance =

new

Singleton();

這段代碼進行同步,如果對象已經被建立,就不會進入到第一個if代碼段裡面,是以僅僅在第一次建立的時候會進行同步,效率自然高了。現在看來萬無一失了。但問題還沒有這麼簡單。

out-of-order write問題,更多相關資料請見: DoubleCheckedLocking

instance = new Singleton();  的順序應該是

1 2 3

1

配置設定記憶體

2

構造函數初始化

3

将對象的reference指派給instance

但因為Java Memory Model的問題,可能出現下面的所謂out-of-order write的問題:

1 2 3

1

配置設定記憶體

2

将對象的reference指派給instance

3

構造函數初始化

也就是還沒對對象初始化,就已經instance != null了,這樣如果另外一個線程這時候對執行個體進行操作,可能有意想不到的結果。

但仍舊沒有好的辦法可以完全解決這個問題。見 參考一 , 參考二

綜上,采用Snippet 1或者Snippet 3比較安全。Snippet 2和Snippet 4最好在多線程的環境下不要使用,否則可能會出錯。

4 限制

但Singleton的模式還是有限制的

1 因為采用private constructor,是以Singleton是不能被繼承的。

2 如果應用是在容器中運作,就要小心,因為servlet可能在被幾個classloader加載,同時有幾個Singleton執行個體存在。

3 另外如果Singleton是可被序列化的(Serializable),如果序列化一次而反序列化多次就有可能有多個Singleton執行個體存在。

5 結論

是以就算是看起來最簡單的設計模式也有這麼多變數,一不小心就可能落入陷阱。不過當你知道陷阱在哪,也就能避免掉進去了。

使用将構造方法封裝的方法:無論産生多少個對象,我們隻執行個體化一次,這樣的設計在設計模式上稱為單态設計模式(單例設計模式):

singleton , 如果不希望一個類産生多個執行個體的話,則必須使用單态設計模式,此設計模式,在以後的高性能開發中經常使用,而且在 java 的類庫中大量的使用了此設計模式。

    所謂單态就是在入口處限制了對象的執行個體操作。

    單态設計模式的意義:

實際上這種模式非常常用,我們大家使用的windows作業系統中就使用了此設計模式,windows中用一個回車站: ,除了桌面以外,其他的每個硬碟中都有一個回車站,其他的回車站和其他硬碟的每個回車站都是同一個,也就是所整個作業系統中有且隻有一個回車站,各個地方隻是引用此執行個體。

單态設計模式的核心就是将類的構造函數私有化,在類的内部産生執行個體對象,并通過類的靜态方法傳回類的執行個體對象。