設計模式學習筆記之單例模式

birdlove1987發表於2017-02-19

設計模式中最簡單的模式就要數單例模式了。

那麼什麼是單例模式呢? 保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。



上圖為單例模式的結構示意圖。


那麼為什麼要使用單例模式呢?

簡單來說:單例模式的存在,一則,解決多執行緒併發訪問的問題二則,節約系統記憶體,提交系統執行的效率,提高系統效能


下面我們就來看看程式中是怎麼實現單例模式的:


一般我們設計一個類都是這樣的:


public class Abc
{
	private Abc()
	{
			
	};	
}


然後在其他部分呼叫:


public class Cbd
{
	public Cbd()
	{
		Abc n1,n2;
		n1=new Abc();
		n2=new Abc();
	}
}


但這樣Abc這個類就會出現好多實體並不好控制,下面我們來改變一下啊Abc這個類的結構從而實現單例模式:


public class Abc{
	
	private static Abc uniqeInstance_Abc = null; //通過內建私有靜態引數實現單例狀態
	
	private Abc(){
		
	};
	
	//外部只能呼叫單例,單例實體有類內部控制
	public static Abc getInstance_Abc()
	{
		if(uniqeInstance_Abc==null)
		{
			uniqeInstance_Abc=new Abc();
		}
		return uniqeInstance_Abc;
		
	}

}


這樣我們變實現了單例模式


public class Cbd
{
	public Cbd()
	{
		Abc n1,n2;
		n1=Abc.getInstance_Abc();
	}
}


可以看到Abc這個類的建構函式變成了private修飾,所以並不能在其他類中new出來只能通過呼叫getxxx函式來使用,這就保證了單例性。


寫一個小例子,模擬一下我們計算機中的CPU處理程式:




package com.java.jikexueyuan.singleton;

public class Cpu {
	private boolean idle;
	private boolean compute;
	public volatile static Cpu uniqueInstance_cpu = null;
	int num;

	private Cpu() {
		idle = true;
		compute = false;
		num = 101;
	}

	public static Cpu getInstance() {

		if (uniqueInstance_cpu == null) {
			synchronized (Cpu.class) {
				if (uniqueInstance_cpu == null) {
					uniqueInstance_cpu = new Cpu();
				}
			}
		}
		return uniqueInstance_cpu;
	}

	//判斷cpu是否為空閒狀態
	public boolean isIdle() {
		if (idle) {
			idle = false;
			compute = false;
			System.out.println("cpu空閒");
			return true;
		}
		else
		{
			System.out.println("cpu繁忙");
			return true;
		}
	}
	
	//cpu計算結束
	public void compute_over() {
		if ((!idle) && compute) {
			idle = true;
			compute = false;
			System.out.println("cpu計算完成");
		}
	}
	
	//啟動cpu計算過程
	public void computing() {
		if ((!idle) && (!compute)) {
			compute = true;
			System.out.println("cpu正在計算,計算cpu編號為" + num);
		}
	}
}

我們來呼叫一下:


public class test {
	public static void main(String[] args) 
	{
		for(int i = 0; i < 5 ; i++)
		{
			Cpu c1 = Cpu.getInstance();
			if(c1.isIdle())
			{
				c1.computing();
				c1.compute_over();
			}
		}
	}
}



其實這樣設計的單例模式有一個重大隱患,就是當兩個執行緒同時建立一個Abc類的時候,可以會new出來兩個實體而導致嚴重的錯誤。。。


解決傳統的解決方式呢,有三種:

第一種:新增同步鎖:


public class Abc{
	
	private static Abc uniqeInstance_Abc = null; //通過內建私有靜態引數實現單例狀態
	
	private Abc(){
		
	};
	
	//外部只能呼叫單例,單例實體有類內部控制
	//新增了同步所,保證不會有兩個執行緒同是進入該方法
	public static synchronized Abc getInstance_Abc()
	{
		if(uniqeInstance_Abc==null)
		{
			uniqeInstance_Abc=new Abc();
		}
		return uniqeInstance_Abc;
		
	}

}


第二種:急切建立法:

public class Abc{
	
	//通過內建私有靜態引數實現單例狀態
	//直接建立例項
	private static Abc uniqeInstance_Abc = new Abc();
	
	private Abc(){
		
	};
	
	//外部只能呼叫單例,單例實體有類內部控制
	public static Abc getInstance_Abc()
	{
		if(uniqeInstance_Abc==null)
		{
			uniqeInstance_Abc=new Abc();
		}
		return uniqeInstance_Abc;
		
	}

}


第三種:雙重檢查加鎖法


public class Abc{
	
	//通過內建私有靜態引數實現單例狀態
	//volatile關鍵字確保多執行緒在處理時的正確性
	private volatile static Abc uniqeInstance_Abc = new Abc();
	
	private Abc(){
		
	};
	
	//外部只能呼叫單例,單例實體有類內部控制
	public static Abc getInstance_Abc()
	{
		//多重加鎖
		if (uniqeInstance_Abc == null) {
			synchronized (Abc.class) {
				if (uniqeInstance_Abc == null) {
					uniqeInstance_Abc = new Abc();
				}
			}
		}
		return uniqeInstance_Abc;
	}
}


單例模式的使用場景:

1.資源共享的情況下,避免由於資源操作時導致的效能或損耗等。如上述中的日誌檔案,應用配置。 
2.控制資源的情況下,方便資源之間的互相通訊。如執行緒池等。 


相關文章