1. 程式人生 > >設計模式之單例模式分析

設計模式之單例模式分析

詳細瞭解單例模式。 單例模式的定義是什麼? 確保一個類只有一個例項,而且自行例項化並向整個系統提供這個例項。 它有幾個要素: 1、私有的構造方法(有些人可能會忘記) 2、指向自己例項的私有的靜態的引用 3、以自己例項為返回值的靜態的公用的方法 單例模式的優點是什麼? 1、記憶體中只有一個物件,節省記憶體空間 2、避免頻繁的建立銷燬物件,可以提高效能 3、避免對共享資源的多重佔用 4、可以全域性訪問 注意事項:不要使用反射,否則會例項化一個新的物件 程式碼如下:

class c = Class.forName(MySingleton.class.getName());
Constructor ct = c.getDeclaredConstructor();
et.setAccessible(true);
Singleton sy = (Singleton)  ct.newMySingleton;

單例模式根據例項化物件的時機分為餓漢單例模式和懶漢單例模式。 餓漢單例模式是在類載入時就例項化一個物件,懶漢單例模式是在呼叫獲得例項化物件的方法時才例項化物件 寫一個餓漢單例模式:

    public class MySingleton{
    	private static Singleton sy = new Singleton(); //指向自己例項的私有靜態引用
    	private  MySingleton{}   //私有的構造方法
    	public static SingletongetMySingleton(){ //以自己例項為返回值的靜態公有方法
    			return sy; 
    }
}

再來一個懶漢式單例模式:

public class MySingleton{
	private static Singleton sy;
	private MySingleton{}
	public static Singleton getMySingleton(){//當呼叫我的取得例項物件的方法時我才會去例項化物件
			if(sy == null){
				sy = new Singleton();//例項化物件
			}
			return sy;	
	}
}

上面的懶漢式單例執行緒是否安全? 不安全的,因為多執行緒去呼叫取得例項物件的方法時,第一個執行緒還沒有執行例項化物件操作時,後面的執行緒同樣也可以去例項化物件,最後會例項化兩個物件,這樣就造成了執行緒不安全。 解決執行緒安全的原則是什麼?在多執行緒環境中,能永遠保證程式的正確性。 那麼懶漢式單例模式要確保執行緒安全就需要使每次呼叫取得例項物件的方法時我們只生成一個物件。 解決方法: 1、利用synchronized關鍵字來實現 ,但是這種寫法的效率很低,因為每次呼叫getMySingleton方法的時候都必須獲得Singleton的鎖,而實際上,當單例例項被建立以後,其後的請求沒有必要再使用互斥機制了.

   public static synchronized Singleton getMySingleton(){
        			if(sy == null){
        				sy = new Singleton();
       }

2、利用volatile 和synchronized來實現

public class MySingleton{
    	private volatile static  Singleton sy;
    	private MySingleton{}
    	public static Singleton getMySingleton(){
    			if(sy == null)
            				synchronized (sy) {
    						if(sy == null)
    						sy = new Singleton();
    						}
    	}
    }

為什麼加了volatile關鍵字就能確保雙重鎖的實現? 除了它可以保證可見性之外,使用volatile 的主要原因是其另一個特性:禁止指令重排序優化。 3、雙重鎖機制寫法

public static  Singleton getMySingleton(){
        			if(sy == null)
        				synchronized (sy) {
						if(sy == null)
						sy = new Singleton();
						}
	}

對於JVM而言,它執行的是一個個Java指令。在Java指令中建立物件和賦值操作是分開進行的,也就是說sy = new Singleton();語句是分兩步執行的。但是JVM並不保證這兩個操作的先後順序,也就是說有可能JVM會為新的Singleton例項分配空間,然後直接賦值給sy成員,然後再去初始化這個Singleton例項。(即先賦值指向了記憶體地址,再初始化)這樣就可能會造成多個執行緒去呼叫取得例項化物件方法時返回了空的情況。

4、利用內部類來解決執行緒安全的問題(推薦使用)

public class MySingleton{        
    private MySingleton(){             
    }        
    private static class SingletonContainer{        
        private static Singleton sy = new Singleton();        
    }        
    public static Singleton getInstance(){        
        return SingletonContainer.sy;        
    }        
}