1. 程式人生 > >閱讀Head First 設計模式之單例模式

閱讀Head First 設計模式之單例模式

    單例模式,顧名思義,就是一個類只能建立一個例項並提供一個全域性訪問點

    該怎麼實現單例模式那?

    第一步:在JAVA面向物件的語言中,要想建立一個例項物件,只需new Object()即可,但正常情況下我們可以新建許多個例項物件。例項化一個物件時其實是例項化其構造方法,構造方法預設作用域是public的,這個導致該物件可以被外部例項化而沒有限制,若該類構造方法的作用域改成private,我們只能在該類內部來新建例項物件。

    第二步:既然第一步確保了新建例項物件是自己可控後,怎麼確保只能建立一個例項物件?由於外部物件不能例項化該物件,我們需要提供一個靜態的獲取例項的方法供外部呼叫,可以在該方法中控制只能例項化一次。

    一.懶漢式單例模式

      1.1  只有在呼叫類的例項化方法時,才建立例項物件,執行緒不安全。

public class Singleton {

	// 利用一個靜態變數來記錄Singleton的唯一例項
	private static Singleton uniqueInstance;

	// 把構造器宣告為私有,只有來自Singleton類內才可以呼叫構造器
	private Singleton() {
	}

	// 提供公共的靜態例項方法供外部呼叫,並返回Singleton類的唯一例項
	public static Singleton getInstance() {
		if (uniqueInstance == null) {
			uniqueInstance = new Singleton();
		}
		return uniqueInstance;
	}

}

 通過多執行緒環境測試是否只產生一個例項:

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			new Thread(new Runnable() {

				@Override
				public void run() {
					System.out.println(Singleton.getInstance());
				}
			}).start();
		}

	}

結果顯示並不能保證執行緒安全,不一定只產生一個例項:

1.2 為了保證執行緒安全,在犧牲效能的情況下,通過getInstance方法加同步

public class Singleton {

	// 利用一個靜態變數來記錄Singleton的唯一例項
	private static Singleton uniqueInstance;

	// 把構造器宣告為私有,只有來自Singleton類內才可以呼叫構造器
	private Singleton() {
	}

        // 提供公共的靜態例項方法供外部呼叫,並返回Singleton類的唯一例項
	// 為了保證執行緒安全,加了同步程式碼塊
	public synchronized static Singleton getInstance2() {
		if (uniqueInstance == null) {
			uniqueInstance = new Singleton();
		}
		return uniqueInstance;
	}

}

結果顯示能保證執行緒安全,只產生一個例項:

    1.3 以上方法雖然保證只產生一個例項,但效能差,於是改成同步程式碼塊並加雙重鎖判斷

public class Singleton {

	// 利用一個靜態變數來記錄Singleton的唯一例項
	private static Singleton uniqueInstance;

	// 把構造器宣告為私有,只有來自Singleton類內才可以呼叫構造器
	private Singleton() {
	}

	// 提供公共的靜態例項方法供外部呼叫,並返回Singleton類的唯一例項
	// 同步程式碼塊並加雙重鎖判斷,即保證執行緒安全,效能也不會差
	public static Singleton getInstance3() {
		if (uniqueInstance == null) {
			synchronized (Singleton.class) 
			{
				if(uniqueInstance == null) {
					uniqueInstance = new Singleton();
				}
			}
		}
		return uniqueInstance;
	}

}

二.餓汗式單例模式

在類載入時,就獲取到類的例項,能保證執行緒安全,但不管是否需要,都會獲取類的例項,影響效能。

public class Singleton {

	// 利用一個靜態變數來記錄Singleton的唯一例項,載入類時就獲取
	private static Singleton uniqueInstance = new Singleton();

	// 把構造器宣告為私有,只有來自Singleton類內才可以呼叫構造器
	private Singleton() {
	}

	// 提供公共的靜態例項方法供外部呼叫,並返回Singleton類的唯一例項
	// 惡漢式
	public static Singleton getInstance4() {
		return uniqueInstance;
	}

}

 

三.靜態內部類單例模式

                首先利用jvm的載入機制保證只能產生一個例項,其次靜態內部類也是一個單獨的class檔案,只有在使用時才載入,保證了效率。書中並沒提及該方法,通過網上查詢資料找的。

public class Singleton {

	// 利用靜態內部類來記錄Singleton的唯一例項
	private static class SingletonHolder {
		private static Singleton uniqueInstance = new Singleton();
	}

	// 把構造器宣告為私有,只有來自Singleton類內才可以呼叫構造器
	private Singleton() {
	}
	
	// 提供公共的靜態例項方法供外部呼叫,並返回Singleton類的唯一例項
	public static Singleton getInstance5() {
		return SingletonHolder.uniqueInstance;
	}

}

 

總結: 使用單例模式,為了兼顧執行緒安全與效能,推薦使用同步程式碼塊並加雙重鎖判斷的懶漢式或靜態內部類的形式。

參考:https://blog.csdn.net/twocold_2010/article/details/53241056