1. 程式人生 > >java設計模式---(1)單例模式

java設計模式---(1)單例模式

單例模式
定義: 確保一個類只有一個例項, 而且自行例項化並向整個系統提供這個例項
保證只有一個例項,只能是不讓外面訪問的類的構造方法,所以就構造方法寫成私有的,向整個系統提供一個例項那麼就只能定義一個static的例項返回回去了,所以程式碼可以這樣寫:

public class Singelton {

	private static Singelton singelton = new Singelton();

	private Singelton(){ // 構造方法私有化,就不能new一個Singelton物件了
	}
	
	public static Singelton getSingelton() { // 通過getSingelton()來獲取已經準備好了的物件
		return singelton;
	}
}

驗證結果:

	public static void main(String[] args) {
		Singelton s1 = Singelton.getSingelton();
		Singelton s2 = Singelton.getSingelton();
		System.out.println(s1.equals(s2)); // true
	}

結果說明2次呼叫返回的是同一個物件,其實這個沒什麼好驗證的,static 修飾的singelton物件在記憶體裡面必然是獨一份。
上面這個寫法就是通常所說的飽漢模式,所謂飽漢模式就是物件事先已經new好了,就像是吃飽了的。對應的就有個餓漢模式了,餓漢模式

是事先不new這個物件(先餓著),等到用的時候再new(要說這樣有什麼好呢,大約就是可以在類載入的時候少佔的資源,畢竟少例項化一個static物件),其他地方跟上面的飽漢模式一樣。
於是把上面的飽漢模式改一下,成了下面這樣:

public class SingeltonHungry {

	private static SingeltonHungry hungry = null;

	private SingeltonHungry() { // 構造方法私有化,就不能new一個Singelton物件了
	}

	public static SingeltonHungry getSingelton() { // getSingelton()來獲取物件,如果沒有就建立,有就直接返回
		if (hungry == null) {
			hungry = new SingeltonHungry();
		}
		return hungry;
	}
}

在單執行緒下就不驗證了,跟上面一樣,返回的都是一個物件。但是在多執行緒的情況下就不一樣了,因為有個hungry == null判斷,然後才new一個新物件。所以在hungry物件最開始是null的時候如果2個執行緒同時執行上面的判空,然後都是true,然後就都去new了自己的物件了,這就不是單例了。
先寫個執行緒類:

public class TestThread implements Runnable {
	@Override
	public void run() {
		SingeltonHungry singelton = SingeltonHungry.getSingelton();
		System.out.println("SingeltonHungry的hashCode:"+singelton.hashCode());
	}
}

然後來進行測試:

public static void main(String[] args) {
	Thread t1 = new Thread(new TestThread());
	Thread t2 = new Thread(new TestThread());
	t1.start();
	t2.start();
}

輸出結果是:

SingeltonHungry的hashCode:1734172372
SingeltonHungry的hashCode:1232133915

說明2個執行緒各自建立了一個例項。所以這樣的話就不是單例了。所以要改一下,給getSingelton()方法加個同步鎖:

public class SingeltonHungry {
	private static SingeltonHungry hungry = null;

	private SingeltonHungry() { // 構造方法私有化,就不能new一個Singelton物件了
		System.out.println("call constructor"); // 看呼叫了幾次構造方法
	}

	public static synchronized SingeltonHungry getSingelton() { // getSingelton()來獲取物件,如果沒有就建立,有就直接返回
		if (hungry == null) {
			hungry = new SingeltonHungry();
		}
		return hungry;
	}
}

再次使用上面的測試方法來測試一下2個執行緒來建立例項的情況,輸出結果是:

call constructor
SingeltonHungry的hashCode:1481297610
SingeltonHungry的hashCode:1481297610

只輸出一次“call constructor” 並且兩個執行緒得到的物件是hashCode是一樣的,這足以說明2個執行緒建立最終得到的物件是同一個。
總結一下,單例模式3個點:
1.私有化構造方法,讓別人不能自主例項化物件
2.自己提供一個方法來建立一個靜態物件。
3.單例模式有2個型別:飽漢模式、餓漢模式(要注意多執行緒問題)