Singleton 單例設計模式

---------------------- android培訓java培訓、期待與您交流! ----------------------

 

開發中,常用到Singleton 單例設計模式,他解決了一個類在內存中只能有一個對象,那麼怎樣保證對象的唯一性呢?

我們可以把這個類唯一的構造函數私有化,讓他不能創建對象,也不能被繼承,那怎樣創建對象呢?只能在本類中先創建對象,

然後對外提供一個公共的獲取實例的方法。

先看第一種寫法,餓漢式的寫法:

public class Singleton {
    private static Singleton instance = new Singleton();  
    //other fields…

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }
  
    //other methods…



1、  Singleton類的只有一個構造方法,它是被private修飾的,客戶對象無法創建該類實例。 
2、 我們爲此單例實現的全局訪問點是public static Singleton getInstance()方法,注意,instance變量是私有的,外界無法訪問的。 我們也可以定義instance變量是public的,這樣把屬性直接暴露給其他對象,就沒必要實現public static Singleton getInstance()方法,但是可讀性沒有方法來的直接,而且把該實例變量的名字直接暴露給客戶程序,增加了代碼的耦合度,如果改變此變量名稱,會引起客戶類的改變。 如果該實例需要比較複雜的初始化過程時,最好把這個過程應該寫在static{…}代碼塊中。 
3、  此實現是線程安全的,當多個線程同時去訪問該類的getInstance()方法時,不會初始化多個不同的對象,這是因爲,JVM(Java Virtual Machine)在加載此類時,對於static屬性的初始化只能由一個線程執行且僅一次[1]
由於此單例提供了靜態的公有方法,那麼客戶使用單例模式的代碼也就非常簡單了,如下所示:
Singleton singleton = Singleton.getInstance(); //這種寫法比較簡單常用,因爲只有一條執行語句,不會產生多線程安全問題,所以也稱作“餓漢式”

 

二、單例設計模式的另一種寫法,懶漢式,從字面上理解,他比較懶,也就是這個類一初始化的時候並不會直接創建該類的實例,只有調用該類獲取對象的方法是,才創建了該類對象,也稱爲對象的延遲加載,俗稱“懶漢式”,那麼我們可以這樣寫:

乍一看沒問題,這個寫法在單線程中確實沒有問題,但是在多線程中,可能出現創建一個以上對象的情況:

class Single
{
 private static Single s = null;
 private Single(){}
 public static Single getInstance()
 {
       if(s==null)       // t1 t2兩個線程
       {                       // Thread.sleep(10);假設 t1 cpu執行到這裏, 這時t2線程進來了,也執行到這裏掛了,t1醒了 new個對象,t2又醒了,他直接向下執行,也new 個對象
           s=new Single();  

        }

       return s;
 }
}

怎麼解決呢?多線程同步函數可以解決,某一時刻,同步函數內只能有一個線程執行,必須這個線程執行完,另一個線程才能進來

class Single
{
 private static Single s = null;
 private Single(){}
 public static synchronized  Single getInstance()
 {
       if(s==null)      

       {                      
           s=new Single();  

      }

     return s;

 }
}

這樣寫就安全了,但程序的效率也大大降低了,因爲任何一個線程進來都要判斷鎖,怎樣才能減少判斷鎖的次數呢?我們只要把多個線程要訪問的共享數據加鎖就行了,

沒必要整個函數都加鎖,這樣寫:

class Single
{
 private static Single s = null;
 private Single(){}
 public static Single getInstance()
 {
  if(s==null)  //一旦new 對象,這就不爲null,就不再判斷鎖了,相比同步函數大大提高了效率
  {
   synchronized(Single.class)   //靜態的同步方法,使用的鎖是該方法所在類的字節碼文件對象。即類名.class
   {    
    if(s==null)
     s = new Single();
   }
  }
  return s;
 }
}

最後對單例設計模式的總結:

單例模式有兩種:一種是餓漢式,另一種是懶漢式。
有什麼區別(面試):懶漢式的特點在於實例的延遲加載。
懶漢式延遲加載有什麼問題:有,如果多線程訪問時會出現安全問題,怎麼解決,可以加同步來解決;可以加同步來解決,用同步代碼塊和同步函數都行,但稍微有些低效,用雙重判斷能解決效率問題。
那麼加同步的時候使用的鎖是哪一個呢?該類所屬的字節碼文件對象。

注意:這裏是在同一個JVM中,才能保證一個類只有一個單例,如果在分佈式環境中,整個應用(可能分佈在不同JVM上),這樣寫是不OK的。

 

---------------------- android培訓java培訓、期待與您交流! ----------------------詳細請查看:http://edu.csdn.net/heima
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章