1. 程式人生 > >設計模式一:單例模式

設計模式一:單例模式

單例模式是最簡單的一個,也是比較常用的一個。所以,首先就拿它先開刷了,哈哈。

 說起單例模式,有的人覺得沒啥呀,不就是一個類只產生一個物件麼?

是的,沒錯,但是不知道你有沒有避過這些坑呢???

讓我們一塊來看一下吧。。。

一:什麼是單例?有什麼用?應用於什麼時候?

二:經典單例模式的實現原理+類圖

三:例項專案程式碼

四:對經典單例模式坑的優化方案

一:什麼是單例?有什麼用?應用於什麼時候?

單利模式:確保一個類最多隻有一個物件例項,供大家使用。


有些物件,我們只需要一個物件例項:執行緒池,快取,硬體裝置等;

(比如使用印表機:如果物理上是有一個,而new多個印表機物件,被多個人只用,這樣會造成資料混亂,衝突,結果不一致等問題)

--------------那麼問題來了----------

是否可以使用靜態變數方式實現?       -------------目的可以達到

或者程式設計師之間協商出個全域性變數?   -------------可以達到

二:經典單例模式的實現原理+類圖

先上類圖:

實現原理:

public class Singleton {
//宣告一個靜態物件
	private static Singleton uniqeInstance = null;
	//私有化構造方法
	private Singleton() {
	
	}
	//建立獲取物件方法getInstance();
	public static Singleton getInstance(){
		//判斷物件是否建立
		if(uniqeInstance ==null){
			//建立物件
			uniqeInstance = new Singleton();
		}
		
		//返回物件
		return uniqeInstance;
		
	}
}

三:例項專案程式碼

初級版:

public class ChocolateFactory1 {
	// 宣告封裝的私有屬性
	private boolean empty;
	private boolean boiled;
	// 宣告靜態物件
	public volatile static ChocolateFactory1 uniqueInstance = null;

	// 私有化構造方法
	private ChocolateFactory1() {
		empty = true;
		boiled = false;
	}

	// 建立獲取物件的方法
	public static ChocolateFactory1 getInstance() {
		// 判斷
		if (uniqueInstance == null) {
			// 生成物件
			uniqueInstance = new ChocolateFactory1();
		}
		// 返回物件
		return uniqueInstance;

	}

	// 製作流程中的新增原料的方法:
	public void fill() {
		if (empty) {
			// 新增原料巧克力動作
			empty = false;
			boiled = false;
		}
	}

	// 製作流程中的熬煮的方法:
	public void boil() {
		if ((!empty) && (!boiled)) {
			// 煮沸
			boiled = true;
		}
	}

	// 製作流程中的切割排出巧克力的方法:
	public void drain() {
		if ((!empty) && boiled) {
			// 排出巧克力動作
			empty = true;
		}
	}
}

相信大夥看過之後,沒啥問題啊。Are you Fuck  keeding me?  別上火,真沒 逗你們。

看下圖哈-------

大佬們,明白了麼?

別捉急,聽我細說------

上面那一段,當我們面對多執行緒訪問的時候,問題就來了,

比如一個極端情況(A執行緒剛剛判斷完確定沒有物件例項建立,正去建立,就是這會兒。卡-----自己的時間片用完了,切換到B執行緒進來了,一看也是要建立物件,而且建立完物件走了,招呼都沒打。等到切換A執行緒,它還是繼續自己走之前的那一步(去建立個物件)

這個流程下來,,他們倆就建立了倆物件,不安全吧,是個坑吧,咋弄啊。

四:對經典單例模式坑的優化方案

別捉急,咱有的是辦法,繼續看--------(方法有三)

1:加個同步鎖;

// 建立獲取物件的方法
	// 同步鎖--只准許一個進,出來之後,第二才能進
	
		public static synchronized ChocolateFactory getInstance() {
			
					if (uniqueInstance == null) {
						//建立物件
						uniqueInstance = new ChocolateFactory();
					}
			// 返回物件
			return uniqueInstance;

		}


有了同步鎖,就可以避免了多個執行緒同時訪問的情況。但是同步鎖是消耗資源的,尤其是當很多很多執行緒很多次很多次訪問。

(沒鬧,可能真的就有這種)那就再看看下面的方法,哈哈。

2:加急物件法;

public class ChocolateFactory {

	// 宣告封裝的私有屬性
	private boolean empty;
	private boolean boiled;
	// 宣告靜態物件
	public volatile static ChocolateFactory uniqueInstance = new ChocolateFactory();

	// 私有化構造方法
	private ChocolateFactory() {
		empty = true;
		boiled = false;
	}

	// 建立獲取物件的方法
		public static ChocolateFactory getInstance() {
			
					if (uniqueInstance == null) {
						//建立物件
						uniqueInstance = new ChocolateFactory();
					}
			// 返回物件
			return uniqueInstance;

		}


當然,還是那句話,如果我不想使用這個類的物件,他還是給我建立好了,無奈啊。記憶體資源又讓它給敗家了,別急,咱還有招兒

3:雙重檢查鎖;

public class ChocolateFactory {

	// 宣告封裝的私有屬性
	private boolean empty;
	private boolean boiled;
	// 宣告靜態物件  volatile --針對編譯器使用的,處理多執行緒安全問題的關鍵字
	public volatile static ChocolateFactory uniqueInstance = null;

	// 私有化構造方法
	private ChocolateFactory() {
		empty = true;
		boiled = false;
	}

	// 建立獲取物件的方法
		public static ChocolateFactory getInstance() {
			// 判斷
			if (uniqueInstance == null) {
				// 同步鎖--只准許一個進,出來之後,第二才能進
				synchronized (ChocolateFactory.class) {
					//再次判斷
					if (uniqueInstance == null) {
						//建立物件
						uniqueInstance = new ChocolateFactory();
					}
				}
			}
			// 返回物件
			return uniqueInstance;

		}

	public void fill() {
		if (empty) {
			// 新增原料巧克力動作
			empty = false;
			boiled = false;
		}
	}

	public void drain() {
		if ((!empty) && boiled) {
			// 排出巧克力動作
			empty = true;
		}
	}

	public void boil() {
		if ((!empty) && (!boiled)) {
			// 煮沸
			boiled = true;
		}
	}
}

博主有話說:

    作為菜鳥的我也是看了很多資料,在這裡總結整理一個,囉裡囉嗦的 ,彆著急,我就是個話癆。哈哈,希望能給後面的兄弟帶來幫助,也希望能有大佬帶我飛,我要和太陽肩並肩。