設計模式與實踐:創建型模式(單例模式)

創建型模式

  • 單例模式
  • 工廠模式
  • 建造者模式
  • 原型模式
  • 對象池模式

單例模式

單例模式(Singleton pattern)是最常用的設計模式,它具有易於理解、使用簡便等特點。有時候單例模式會過度使用或者在不合適的場景下使用,這樣造成弊大於利的後果。

單例模式,顧名思義,用來保證一個對象只能創建一個實例,此外,它還提供了對實例的全局訪問方法。

在這裏插入圖片描述
單例模式的實現很簡單,只需要由單個類組成。爲確保單利實例的唯一性,所有的單利構造器都需要被聲明爲私有的(private),再通過聲明靜態(static)方法實現全局訪問獲得該單例實例。

public class Singleton
{
	private static Singleton instance;
	private Singleton()
	{
		//TODO
	}
	public static Singleton getInstance()
	{
		if(null == instance){
			instance = new Singleton();
		}
		return instance;
	}

	public vid doSomething()
	{
		//TODO
	}
}

當我們在代碼中使用該單例對象的時候,調用方式如下:

Singleton.getInstance().doSomething();

1. 同步鎖單例模式

單例模式的實現很簡單且高效,但是在多線程中的應用卻不能這樣隨意,如果實例爲空,那麼就會有出現兩個或者多個線程同時調用getInstance()的情況。這就會出現實例化多個對象的情況出現。

解決辦法很簡單,我們只需要創建一個代碼塊來檢查實例是否空線程安全。

  • 向getInstance方法的聲明中添加synchronized關鍵字來保證其線程安全:
public static synchronized Singleton getInstance(){
	//TODO
}
  • 用synchronized代碼塊包裝 if( instance == null ) 條件。在這一環境中使用synchronized代碼塊時,需要制定一個對象來提供鎖,Singleton.class對象就起這種作用。
synchronized (Singleton.class){
	if(instance == null){
		instance = new Singleton();
	}
}

2. 擁有雙重校驗鎖機制的同步鎖單例模式

上一個實現方式可以保證線程安全,但同時帶來了延遲。用來檢查實例是否被創建的代碼塊是線程同步的,也就是這個代碼塊同一時刻只能被一個線程執行,但是同步鎖(locking)只有在實例沒有被創建的情況下才起作用,如果單例實例已經被創建了,那麼任何線程都希望能夠用非同步的方式獲取當前的實例。

只有在單例模式爲實例化的情況下,才能在synchronized代碼塊前添加附加條件移動線程安全鎖:

if(instance == null){
	synchronized(Singleton.class){
		if(instance == null){
			instance = new Singleton();
		}
	}
}

這是個很聰明的校驗方式,注意到instance==null條件被檢查了兩次,因爲我們需要保證synchronized代碼塊中也要進行一次檢查

3. 無鎖的線程安全單例模式

如何不通過鎖來使得單例模式線程安全呢?

Java中單例模式的最佳實踐中,類只會加載一次,通過聲明時直接實例化靜態成員的方式來保證一個類只有一個實例。這種實現方式避免了使用同步鎖和判斷實力是否被創建的額外檢查:

public class LockFreeSingleton{
	private static final LockFreeSingleton instance = new LockFreeSingleton();
	private LockFreeSingleton(){
		//TODO
	}

	//這裏的synchronized的意義在哪裏呢?《設計模式與實踐》書中有synchronized,但是筆者暫時沒讀懂這個synchronized的意義
	public static synchronized LockFreeSingleton getInstance(){
		return instance;
	}
	public void doSomething(){
		//TODO
	}
}

4. 提前加載和延遲加載

上述方法,在早期版本的Java中被認爲是提前加載單例模式,但是在新版本的Java中,類只有在使用的時候纔會被加載,所以它算是一種延遲加載模式。此外,類加載的時機主要取決於JVM的實現機制,因而版本之間會有不同。所以進行設計的時候,要避免與JVM的實現機制進行綁定。

如果確實需要提前實例化,可以在程序的開始通過調用getInstance方法強制執行。

Singleton.getInstance();

參考(學習筆記來自於):
《Java設計模式與實踐》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章