設計模式之一:單例模式

設計模式之一:單例模式
目錄介紹
1.單例模式介紹
2.單例模式定義
3.單例模式使用場景
4.單例模式的實現方式php

  • 4.1 懶漢式【線程不安全】
  • 4.2 懶漢式【**synchronized 線程安全】
  • 4.3 餓漢式【線程安全】
  • 4.4 DCL雙重校驗模式【線程安全】
  • 4.5 靜態內部類單例模式【線程安全】
  • 4.6 枚舉單例【線程安全】
  • 4.7 使用容器實現單例模式

5.Android源碼中單例java

  • 5.1 InputMethodManager中使用單例模式
  • 5.2 LayoutInflater使用的單例模式
  • 5.2 經過Context獲取系統級服務的單例模式

6.單例模式總結
7.其餘git

0.本人寫的綜合案例
案例
說明及截圖
模塊:新聞,音樂,視頻,圖片,唐詩宋詞,快遞,天氣,記事本,閱讀器等等
接口:七牛,阿里雲,天行,乾貨集中營,極速數據,追書神器等等github

1.單例模式介紹數據庫

  • 單例模式是應用最廣的模式,也是我最早知道的一種設計模式,在深刻了解單例模式以前,每當遇到如:getInstance()這樣的建立實例的代碼時,我都會把它當作一種單例模式的實現。
  • 單例模式特色設計模式

    • 構造函數不對外開放,通常爲private
    • 經過一個靜態方法或者枚舉返回單例類對象
    • 確保單例類的對象有且只有一個,尤爲是在多線程的環境下
    • 確保單例類對象在反序列化時不會從新構造對象

2.單例模式定義緩存

  • 保證一個類僅有一個實例,並提供一個訪問它的全局訪問點

3.單例模式使用場景安全

  • 應用中某個實例對象須要頻繁的被訪問。
  • 應用中每次啓動只會存在一個實例。如帳號系統,數據庫系統。

4.單例模式的實現方式多線程

  • 4.1 懶漢式【線程不安全】
  • 懶漢式代碼
//懶漢式單例類.在第一次調用的時候實例化本身  
public class Singleton { 
    //私有的構造函數
    private Singleton() {}
    //私有的靜態變量
    private static Singleton single=null; 
    //暴露的公有靜態方法 
    public static Singleton getInstance() { 
        if (single == null) {   
            single = new Singleton(); 
        }   
        return single; 
    } 
}
  • 代碼分析
懶漢式(線程不安全)的單例模式分爲三個部分:私有的構造方法,私有的全局靜態變量,公有的靜態方法。
起到重要做用的是靜態修飾符static關鍵字,咱們知道在程序中,任何變量或者代碼都是在編譯時由系統自動分配內存來存儲的,而所謂靜態就是指在編譯後所分配的內存會一直存在,直到程序退出內存纔會釋放這個空間,所以也就保證了單例類的實例一旦建立,便不會被系統回收,除非手動設置爲null。
  • 優缺點
優勢:延遲加載(須要的時候纔去加載)
缺點: 線程不安全,在多線程中很容易出現不一樣步的狀況,如在數據庫對象進行的頻繁讀寫操做時。
  • 4.2 懶漢式【synchronized 線程安全】
  • 懶漢式代碼
public class Singleton { 
    //私有的靜態變量
    private static Singleton instance; 
    //私有的構造方法
    private Singleton (){};
    //公有的同步靜態方法
    public static **synchronized** Singleton getInstance() { 
        if (instance == null) { 
            instance = new Singleton(); 
        } 
        return instance; 
    } 
}
  • 代碼分析
這種單例實現方式的getInstance()方法中添加了synchronized 關鍵字,也就是告訴Java(JVM)getInstance是一個同步方法。
同步的意思是當兩個併發線程訪問同一個類中的這個synchronized同步方法時, 一個時間內只能有一個線程獲得執行,另外一個線程必須等待當前線程執行完才能執行,所以同步方法使得線程安全,保證了單例只有惟一個實例。
  • 優缺點
優勢:解決了線程不安全的問題。
缺點:效率有點低,每次調用實例都要判斷同步鎖
它的缺點在於每次調用getInstance()都進行同步,形成了沒必要要的同步開銷。這種模式通常不建議使用。
  • 4.3 餓漢式【線程安全】
  • 餓漢式代碼
//餓漢式單例類.在類初始化時,已經自行實例化   
public class Singleton { 
    //static修飾的靜態變量在內存中一旦建立,便永久存在
    private static Singleton instance = new Singleton(); 
    private Singleton (){} 
    public static Singleton getInstance() { 
        return instance; 
    } 
}
  • 代碼分析
餓漢式在類建立的同時就已經建立好一個靜態的對象供系統使用,之後再也不改變,因此天生是線程安全的。其中instance=new Singleton()能夠寫成:
static { 
    instance = new Singleton(); 
}
  • 4.4 DCL雙重校驗模式
  • DCL雙重校驗模式代碼
public class Singleton {
    private static Singleton singleton;  //靜態變量
    private Singleton (){}  //私有構造函數
    public static Singleton getInstance() {
        if (singleton == null) {  //第一層校驗
            synchronized (Singleton.class) {
                if (singleton == null) {  //第二層校驗
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
  • 代碼分析
這種模式的亮點在於getInstance()方法上,其中對singleton 進行了兩次判斷是否空,第一層判斷是爲了不沒必要要的同步,第二層的判斷是爲了在null的狀況下才建立實例。
  • 優缺點
優勢:在併發量很少,安全性不高的狀況下或許能很完美運行單例模式
缺點:不一樣平臺編譯過程當中可能會存在嚴重安全隱患。
  • 模擬分析
假設線程A執行到了singleton = new Singleton(); 語句,這裏看起來是一句代碼,可是它並非一個原子操做,這句代碼最終會被編譯成多條彙編指令,它大體會作三件事情:
(a)給Singleton的實例分配內存
(b)調用Singleton()的構造函數,初始化成員字段;
(c)將singleton對象指向分配的內存空間(即singleton不爲空了);
可是因爲Java編譯器容許處理器亂序執行,以及在jdk1.5以前,JMM(Java Memory Model:java內存模型)中Cache、寄存器、到主內存的回寫順序規定,上面的步驟b 步驟c的執行順序是不保證了。也就是說執行順序多是a-b-c,也多是a-c-b,若是是後者的指向順序,而且偏偏在c執行完畢,b還沒有執行時,被切換到線程B中,這時候由於singleton在線程A中執行了步驟c了,已經非空了,因此,線程B直接就取走了singleton,再使用時就會出錯。這就是DCL失效問題。
可是在JDK1.5以後,官方給出了volatile關鍵字,將singleton定義的代碼改爲:
private volatile static Singleton singleton;  //使用volatile 關鍵字
  • 4.5 靜態內部類單例模式
  • 靜態內部類單例模式
public class Singleton {
    private Singleton (){} ;//私有的構造函數
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    //定義的靜態內部類
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();  //建立實例的地方
    }
}
  • 優缺點
優勢:延遲加載,線程安全(java中class加載時互斥的),也減小了內存消耗
  • 代碼分析
當第一次加載Singleton 類的時候並不會初始化INSTANCE ,只有第一次調用Singleton 的getInstance()方法時纔會致使INSTANCE 被初始化。所以,第一次調用getInstance()方法會致使虛擬機加載SingletonHolder 類,這種方式不只可以確保單例對象的惟一性,同時也延遲了單例的實例化。
  • 4.6 枚舉單例
  • 枚舉單例代碼
public enum Singleton {  //enum枚舉類
    INSTANCE; 
    public void whateverMethod() { 

    } 
}
  • 代碼分析
枚舉單例模式最大的優勢就是寫法簡單,枚舉在java中與普通的類是同樣的,不只可以有字段,還可以有本身的方法,最重要的是默認枚舉實例是線程安全的,而且在任何狀況下,它都是一個單例。即便是在反序列化的過程,枚舉單例也不會從新生成新的實例。而其餘幾種方式,必須加入以下方法:才能保證反序列化時不會生成新的對象。
private Object readResolve()  throws ObjectStreamException{
    return INSTANCE;
}
  • 4.7 使用容器實現單例模式
  • 代碼
public class SingletonManager {
  private static Map<String, Object> objMap = new HashMap<String,Object>();//使用HashMap做爲緩存容器
    private Singleton() {
  }
  public static void registerService(String key, Object instance) {
    if (!objMap.containsKey(key) ) {
      objMap.put(key, instance) ;//第一次是存入Map
    }
  }
  public static ObjectgetService(String key) {
    return objMap.get(key) ;//返回與key相對應的對象
  }
}
  • 代碼分析
在程序的初始,將多種單例模式注入到一個統一的管理類中,在使用時根據key獲取對應類型的對象。
  • 5.Android源碼中單例
  • 5.1 InputMethodManager中使用單例模式

  • 5.2 LayoutInflater使用的單例模式
  • 5.2 經過Context獲取系統級服務的單例模式
  • 6.單例模式總結併發

    • 總結:無論以哪一種形式實現單例模式,它們的核心原理是將構造函數私有化,而且經過靜態公有方法獲取一個惟一的實例,在這個獲取的過程當中必須保證線程的安全,同時也要防止反序列化致使從新生成實例對象。
    • 綜合考慮:推薦使用4.4 DCL雙重校驗模式,4.5 靜態內部類單例模式等等
    • 單例對象若是持有Context,那麼很容易引起內存泄漏,此時要注意傳遞給單例對象的Context最好是Application Context

7.其餘說明

相關文章
相關標籤/搜索