懶漢式和餓漢式

首先要肯定這是兩個單列模式。 懶漢式和餓漢式主要區別是建立單列對象的時間不一樣。 懶漢式:之因此叫作懶漢式,是由於只有在須要的時候纔會建立單列對象安全

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.以本身實例爲返回值的公有靜態方法排序

相關文章
相關標籤/搜索