首先要肯定這是兩個單列模式。 懶漢式和餓漢式主要區別是建立單列對象的時間不一樣。 懶漢式:之因此叫作懶漢式,是由於只有在須要的時候纔會建立單列對象安全
public class Singleton{ private Singleton(){}//要有構造方法 private static Singleton singleton = null; //不創建對象,可是要靜態修飾 public static synchronized Singleton getInstance(){ if(singleton == null) { //先判斷是否爲空,若是爲空則直接返回。 singleton = new Singleton (); //懶漢式作法 } return singleton ; } }
餓漢式:無論你何時用,在類建立的時候就已經穿件該實例多線程
public class Singleton{ public Singleton(){} private static Singleton singleton = new Singleton(); //創建對象,靜態變量 public static Singleton getInstance(){ return singleton ;//直接返回單例對象 }}
單列的三個基本點: 1.必須有私有的構造方法。 2.指向本身私有的靜態實例變量。 3.以本身私有實例爲返回值的公開靜態方法。函數
有關線程安全的單例。有一下幾種方法: 1.藉助內部類性能
public class Singleton { private Singleton() { }//私有構造方法 private static class SingletonHolder {//內部類 private final static Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.INSTANCE; } } 屬於懶漢式單例,由於Java機制規定,**內部類SingletonHolder只有在getInstance()方法第一次調用的時候纔會被加載(實現了lazy)**,並且其加載過程是線程安全的。內部類加載的時候實例化一次instance。
2.使用同步方法(懶漢式)線程
public class Singleton { //建立類 private static Singleton instance; //建立一個靜態變量,可是該變量沒有被賦值,也就是沒有實例化 private Singleton (){//私有構造方法 } public static synchronized Singleton getInstance(){ //對獲取實例的方法進行同步 if (instance == null) //若是該變量沒有被實例化 instance = new Singleton(); //直接實例化 return instance;//返回已經實例化的變量 } }
該方法,一次鎖住了一個方法,保證了線程安全。缺點:同步的顆粒度有點大。 每一個synchronized方法都必須得到調用該方法的實例的鎖方能執行,不然所屬線程阻塞,方法一旦執行,就獨佔該鎖,直到從該方法返回時纔將鎖釋放,此後被阻塞的線程方能得到該鎖,從新進入可執行狀態。這種機制確保同一時刻對於每個實例,其所聲明爲synchronized的成員函數中至多隻有一個處於可執行的狀態(由於至多隻有一個可以得到該實例對應的鎖),從而有效的避免了類成員變量的訪問衝突。 也就是該方法不能同時被屢次,只能一個調用完後,另外一個才能調用。日誌
3.雙重檢測方案:code
public class Singleton { private static Singleton instance = null;//靜態變量 private Singleton() { }//私有構造方法 public static Singleton getInstance() { if(instance == null) { synchronzied(Singleton.class) {//同步代碼塊 Singleton temp = instance;//將靜態變量賦值給一個臨時變量 if(temp == null) { temp = new Singleton();//若是臨時變量爲空,則實例化 instance = temp//用臨時變量給靜態變量賦值 } } } return instance; } } //爲什幺引出一個臨時變量,有何意義? //引入一個臨時變量,是同步代碼塊中存在一個數據依賴性。所以能夠避免多線程之間的問題。該代碼是先讀後寫,再賦值 //單線程執行的時候,雖然會指令重排序,可是因爲存在數據依賴性。並不會影響代碼的執行結果 //可是多線程執行的時候,線程與線程之間不存在數據依賴性,所以指令重排序會影響到程序運行的結果 _因爲指令重排序問題,因此不能夠直接寫成下面這樣:_ public class Singleton { private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if(instance == null) { synchronzied(Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; } } //也就是說代碼並非徹底按照咱們所寫的順序一個個的取執行,所以該方法不可行。 可是若是instance實例變量用volatile修飾就能夠了,volatile修飾的話就能夠確保instance = new Singleton();對應的指令不會重排序,以下的單例代碼也是線程安全的: public class Singleton { private static volatile Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if(instance == null) { synchronzied(Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; } }
//之因此使用volatile修飾變量後可使用該方法,是由於volatile能夠禁止指令重排序,所以,使用volatile修飾,可使用該方法對象
單例模式應用的場景通常發如今如下條件下: (1)資源共享的狀況下,避免因爲資源操做時致使的性能或損耗等。如上述中的日誌文件,應用配置。 (2)控制資源的狀況下,方便資源之間的互相通訊。如線程池等。 單例模式要素: a.私有構造方法 b.私有靜態引用指向本身實例 c.以本身實例爲返回值的公有靜態方法排序