【JavaEE學習筆記】設計模式_單例模式

設計模式_單例模式

A.概述

1.概述

單例模式,是整個設計模式中最簡單的,也是開發中最常用的

有些對象,我們只需要一個,比如:線程池,緩存,對話框等

如果被實例化多次,會導致程序出現很多問題

比如:程序行爲異常,資源使用過量,或者結果不一致

2.與全局變量的區別

只創建一次對象,我們也可以直接在成員位置創建對象

也就是全局變量,我們也經常使用

但如果是全局變量,那麼程序已開始運行,就要創建對象

如果在項目中,這個對象非常消耗資源,而這次執行中又沒有使用,那不就很浪費資源

而單利模式不同,只有在使用時,才被調用,並且只創建一次

因此,單例模式確保一個類只有一個實例,並提供一個全局的訪問點

3.單例模式分類

a.同步getInstance():單例模式之懶漢式

b.雙重檢查鎖:單例模式之懶漢式

c.急切實例化:單例模式之餓漢式

4.下面就以一個巧克力工廠類來演示這三種模式

B.同步getInstance()

1.需求分析

巧克力的製作,需要計算機控制鍋爐

將牛奶和巧克力融在一起,然後送到下一階段

現在用代碼模擬巧克力鍋爐

package org.xxxx.oop.singleton;

public class ChocolateBoiler {
	// 空的和煮過的
	private boolean empty;
	private boolean boiled;

	// 代碼開始,鍋爐是空的,也沒煮過
	public ChocolateBoiler() {
		empty = true;
		boiled = false;
	}

	// 給鍋爐添加原料
	public void fill() {
		// 必須是空的才能添加
		if (isEmpty()) {
			empty = false; // 鍋爐滿了
			boiled = false; // 但還沒煮
		}
	}

	// 開始煮
	public void boil() {
		// 判斷必須是滿的,並且煮了
		if (!isEmpty() && isBoiled()) {
			boiled = true; // 煮了
		}
	}

	// 排除
	public void drain() {
		// 判斷不爲空,並且煮了
		if (!isEmpty() && isBoiled()) {
			empty = true; // 排空
		}
	}

	// 判斷鍋爐是否爲空
	public boolean isEmpty() {
		return empty;
	}

	// 判斷是否煮過
	public boolean isBoiled() {
		return boiled;
	}

}

這就是一個完整的操作過程,看上去邏輯也很嚴謹

但我們想一下,如果存在兩個實例,會發生什麼樣的事

一個鍋爐只能是一個對象,因此,我們就要引入單例模式

2.初識單例模式

在每次創建對象時,判斷是否存在,如果存在直接返回

只展示修改的代碼,其他方法不變,參考上一段代碼

package org.xxxx.oop.singleton;

public class ChocolateBoiler {
	
	// 全局變量
	private static ChocolateBoiler cb = null;

	// 私有構造,不能通過外部創建對象
	private ChocolateBoiler() {
	}
	
	// 創建方法,實例化
	public static ChocolateBoiler getInstance() {
		// 判斷是否爲空
		if (cb == null) {
			cb = new ChocolateBoiler();
		}
		
		// 返回該對象
		return cb;
	}

}

實例化問題解決了,我們再分析一下

一個工廠,不可能只有一個鍋爐,會有很多

這麼多鍋爐也不是一個一個工作,肯定是同時工作

這就要考慮多線程,在【JavaSE學習筆記】多線程01這一部分中

引入了窗口賣票這一例子,多個窗口同時售票

會出現同一張票被多個窗口售出,餘票爲負數

線程不安全,爲了使線程安全,因此就引入了同步機制

3.同步getInstance()代碼

package org.xxxx.oop.singleton;

public class ChocolateBoiler {
	
	// 全局變量
	private static ChocolateBoiler cb = null;

	// 私有構造,不能通過外部創建對象
	private ChocolateBoiler() {
	}
	
	// 創建方法,實例化 增加synchronized關鍵字
	public static synchronized ChocolateBoiler getInstance() {
		// 判斷是否爲空
		if (cb == null) {
			cb = new ChocolateBoiler();
		}
		
		// 返回該對象
		return cb;
	}

}

通過增加synchronized關鍵字,迫使每個線程在進入這個方法之前

都必須等到別的線程離開這個方法,也就是說,兩個線程不能同時出現在該方法中

4.缺點

這個方法比較簡單有效,但同步機制可能會使程序執行效率下降100倍

如果使用頻繁的話,就得重新考慮

同步鎖會降低性能,當然對於不考慮性能的程序來說,無關緊要

C.雙重檢查鎖

1.分析

對於同步Instance()來說,會降低程序的性能

我們考慮下,只有在線程第一次進入這個方法時才需要同步

也就是說,當對象被創建時,就不需要再去管同步了

2.volatile關鍵字

首先檢查對象是否被實例化,如果沒有,再進行同步

這就要引入volatile關鍵字,用來確保將變量的更新操作通知到其他線程

在兩個或者更多的線程訪問的成員變量上使用volatile

當要訪問的變量已在synchronized代碼塊中,或者爲常量時,不必使用

當把變量聲明爲volatile類型後,編譯器與運行時都會注意到這個變量是共享的

因此不會將該變量上的操作與其他內存操作一起重排序

volatile變量不會被緩存在寄存器或者對其他處理器不可見的地方

因此在讀取volatile類型的變量時總會返回最新寫入的值。

在訪問volatile變量時不會執行加鎖操作,因此也就不會使執行線程阻塞

因此volatile變量是一種比sychronized關鍵字更輕量級的同步機制

3.當一個變量被volatile定義的特性

a.保證此變量對所有的線程的可見性,這裏的"可見性"

可見性:當一個線程修改了這個變量的值,volatile保證了新值能立即同步到主內存,以及每次使用前立即從主內存刷新

b.禁止指令重排序優化

4.雙重檢查鎖代碼

package org.xxxx.oop.singleton;

public class ChocolateBoiler {
	
	// 全局變量,用volatile修飾
	private volatile static ChocolateBoiler cb = null;

	// 私有構造,不能通過外部創建對象
	private ChocolateBoiler() {
	}
	
	// 創建方法,實例化 增加synchronized關鍵字
	public static ChocolateBoiler getInstance() {
		// 先判斷是否爲空
		if (cb == null) {
			// 對象不存在,開啓同步鎖
			synchronized (ChocolateBoiler.class) {
				// 進入同步機制後,再次判斷一下是否存在
				if (cb == null) {
					// 不存在,創建對象
					cb = new ChocolateBoiler();
				}
			}
		}
		
		// 返回該對象
		return cb;
	}

}
這樣,當只有第一次創建對象時,纔會進入同步機制

因此,大大的減少了getInstance()的時間消耗

5.缺點

在JDK1.4以及更低的版本,本方法不適用

D.急切實例化

1.概述

上述的兩種單例模式方法

同步機制:性能過低

雙重檢查鎖:不適用於舊版本

在此介紹急切實例化,也就是餓漢式,開發中常用,也比較簡單

2.急切實例化代碼

如果程序中總是要創建並使用單利模式

或者在創建和運行時的負擔不太繁重,可以使用該方法

package org.xxxx.oop.singleton;

public class ChocolateBoiler {

	// 全局變量,直接創建對象,靜態初始化器,只執行一次,保證了線程安全、對象唯一
	private static ChocolateBoiler cb = new ChocolateBoiler();
	

	// 私有構造,不能通過外部創建對象
	private ChocolateBoiler() {
	}

	// 創建方法
	public static ChocolateBoiler getInstance() {
		// 返回該對象,對象已經創建了,直接返回
		return cb;
	}

}
保證了在任何線程訪問靜態變量之前,一定先被實例化


發佈了106 篇原創文章 · 獲贊 135 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章