設計模式之一:單例模式
目錄介紹
1.單例模式介紹
2.單例模式定義
3.單例模式使用場景
4.單例模式的實現方式php
5.Android源碼中單例java
6.單例模式總結
7.其餘git
0.本人寫的綜合案例
案例
說明及截圖
模塊:新聞,音樂,視頻,圖片,唐詩宋詞,快遞,天氣,記事本,閱讀器等等
接口:七牛,阿里雲,天行,乾貨集中營,極速數據,追書神器等等github
1.單例模式介紹數據庫
單例模式特色設計模式
2.單例模式定義緩存
3.單例模式使用場景安全
4.單例模式的實現方式多線程
//懶漢式單例類.在第一次調用的時候實例化本身 public class Singleton { //私有的構造函數 private Singleton() {} //私有的靜態變量 private static Singleton single=null; //暴露的公有靜態方法 public static Singleton getInstance() { if (single == null) { single = new Singleton(); } return single; } }
懶漢式(線程不安全)的單例模式分爲三個部分:私有的構造方法,私有的全局靜態變量,公有的靜態方法。 起到重要做用的是靜態修飾符static關鍵字,咱們知道在程序中,任何變量或者代碼都是在編譯時由系統自動分配內存來存儲的,而所謂靜態就是指在編譯後所分配的內存會一直存在,直到程序退出內存纔會釋放這個空間,所以也就保證了單例類的實例一旦建立,便不會被系統回收,除非手動設置爲null。
優勢:延遲加載(須要的時候纔去加載) 缺點: 線程不安全,在多線程中很容易出現不一樣步的狀況,如在數據庫對象進行的頻繁讀寫操做時。
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()都進行同步,形成了沒必要要的同步開銷。這種模式通常不建議使用。
//餓漢式單例類.在類初始化時,已經自行實例化 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(); }
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 關鍵字
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 類,這種方式不只可以確保單例對象的惟一性,同時也延遲了單例的實例化。
public enum Singleton { //enum枚舉類 INSTANCE; public void whateverMethod() { } }
枚舉單例模式最大的優勢就是寫法簡單,枚舉在java中與普通的類是同樣的,不只可以有字段,還可以有本身的方法,最重要的是默認枚舉實例是線程安全的,而且在任何狀況下,它都是一個單例。即便是在反序列化的過程,枚舉單例也不會從新生成新的實例。而其餘幾種方式,必須加入以下方法:才能保證反序列化時不會生成新的對象。 private Object readResolve() throws ObjectStreamException{ return INSTANCE; }
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獲取對應類型的對象。
6.單例模式總結併發
7.其餘說明