您好,登錄后才能下訂單哦!
前兩天寫了設計模式總綱,今天就來講講我們在工程代碼中最最最常用的設計模式了——單例設計模式,這個模式在工程代碼上的出現率幾乎為99.99999%,但是雖然很常用,但是用的好的人卻不多,今天我們就來深入的說一說單例設計模式。
在學習一項新的知識之前,我們都要向自己提出三個問題,為什么要用這個知識,這個知識用在哪里,這個知識怎么用?既 why,where,how,3W模式,我們先來談談為什么需要單例設計模式,先來想想,如果一個工具類,比如一個讀取配置文件的工具類,這類工具只是特定的功能類,既讀取指定的文件內容,這種類我們在使用的時候只需要建造一個就行了,然后在整個系統之中都用這個類來進行指定文件的讀取即可,但是如果在設計之初沒有考慮好,并沒有把其設計成單例,導致整個系統中分布多個類似的功能類,一方面,導致了系統資源的浪費,如果該配置文件內容較小,對內存來說還好,但是如果是幾百M或者幾個G的配置文件的內容的話,就會造成系統資源的嚴重浪費,導致內存泄露,一方面也會讓代碼顯得異常凌亂。
為了解決這種問題,既對于只是解決單一功能的功能類,我們最好的做法就是將其設計成單例,接下來我們來看看我們要怎么來實現一個單例。
正所謂萬丈高樓平地起,再復雜的功能也是由一行行簡單的代碼組成的,那我們來看一下,要實現一個單例類的話,首先,肯定是不能讓用戶自行生產的,那就是說明不能讓用戶new,所以,就必須把構造函數設置成為私有的。
1 public class Singleton {2 private Singleton(){}3 }
好了,這就是單例了,哦,不,這應該是無例,因為把構造函數都弄成私有的了,什么都沒有,用戶拿到了這個類只能一臉懵逼,既然要變成單例,那肯定要給用戶一個實例是吧,既然用戶創建不了,那我們就給他一個,如下
1 public class WorstLazySingleton { 2 //1、私有化構造函數 3 private WorstLazySingleton(){} 4 5 //2、靜態化一個實例,靜態的實例保證了在每一個類中都是唯一的 6 private static WorstLazySingleton instance = null; 7 8 9 //3、返回該對象的實例,也必須是靜態的方法,不然無法調用靜態的實例10 public static WorstLazySingleton getInstance(){11 if(instance == null){12 instance = new WorstLazySingleton();13 }14 return instance;15 }16 }
好了,一個新鮮的單例就出爐了,but,是不是有什么問題呢,為什么這個單例被加上了個Worst的標簽,這個年代什么最慘,被人隨意貼標簽最慘,隔著屏幕都能感受到這個單例哀怨的眼神,但是,我們來看一看,這個單例,咋一看在單線程的環境下沒問題,但是只要一到了多線程的環境下,妥妥的要出問題啊,隨意寫了個測試用例,跑了個10條線程來getInstance,竟然出現了4個不一樣的hashCode,這個哪里是單例,明顯是多的不能再多的”多例“了,好吧,這個worst的標簽就先貼上去吧。那有同學就說了,我加同步方法啊,好,我們來為這個類加上同步方法,
大致如下代碼,
BadLazySingleton instance = //加上了synchronized的同步方法 (instance == instance = }
這個方法現在被加上了synchronized了,運行一下多線程的測試環境,咋一看,好像沒問題了,但是,我們再想一下下面的場景,如果在方法里面這個對象特別大的話,導致虛擬機調用的時間較長,或者在這個方法里面做了其他的 doSomething()方法的話,那其他線程只能乖乖的等待他的結束了,比如這個 方法執行時間用了10S,那10條線程過來,想想就有點小激動呢,一旦運行在服務器端上,那客戶的等待時間,流失率是妥妥的,又有同學要提意見了,我們可以來縮小范圍啊,我們不要再在方法上加同步了,好,那我們來看一看下個version的單例,
1 public class NormalSingleton { 2 //1、私有化構造方法 3 private NormalSingleton(){} 4 5 //2、靜態化一個實例,靜態的實例保證了在每一個類中都是唯一的 6 private static NormalSingleton instance = null; 7 8 public static NormalSingleton getInstance(){ 9 if(instance == null){10 //在局部進行同步,減少線程的等待時間11 synchronized (NormalSingleton.class) {12 //進行雙重判斷,防止線程到了這一步剛好停住了,導致沒有繼續往下走而另外一條線程剛好進來13 if(instance == null){14 instance = new NormalSingleton();15 }16 }17 }18 return instance;19 }20 }
看來這個版本是比較Normal的Singleton了,不僅進行了同步,而且只需要進行一次同步,即只需要在第一次進行同步即可,還涉及到了雙重判斷,防止多線程上環境上的串線,這就是所謂的 Double-Check,but,有人就想到,為什么要我們要自己寫同步,有的人表示已經累覺不愛了,不喜歡自己寫同步了,要榨干JVM的最后一點資源,同步的重任就交給你了,(很用力的拍了拍虛擬機的肩膀),那我們來說一下,什么時候虛擬機會自己給自己加同步。
1、在靜態字段上或static{}塊中的初始化數據時
2、訪問final字段時
3、在創建線程之前創建對象時
4、線程可以可以看見它將要處理的對象時
那有了這四個條件,我們就可以想象,要讓JVM自動來實現同步的話,就可以采用靜態初始化器的方式,但是有人就會說了。靜態初始化器雖然是同步的,但是類一加載的時候他就會去初始化這個對象了,哪怕我們不需要他也會去加載這些對象,那接下來來個腦經急轉彎了,那如果我們可以讓這個類在加載的時候不要去初始化這個對象不就可以嘍?有人會說,有這等好事???
還真有,這種類就叫做靜態內部類,也叫作類級內部類,我們來看看代碼:這種方法算是目前最好的方法之一了:(為什么叫之一....因為還有之二....)
1 public class BestLazySingleton { 2 //私有化構造方法 3 private BestLazySingleton(){} 4 5 //創建靜態的內部類,讓JVM自身來保證線程的安全性,而且該類只有在被調用到的時候才會去加載 6 private static class SingletonHolder { 7 private static BestLazySingleton instance = new BestLazySingleton(); 8 } 9 10 public static BestLazySingleton getInstance(){11 return SingletonHolder.instance;12 }13 }
這個類算是目前最好的懶加載的單例范本了,使用類級內部類,通過jvm來進行同步,當我們調用的時候才去初始化,進而實現了延遲加載的特性,新航道雅思培訓而且也沒有增加同步方法塊,只增加了一個內部域的訪問,成本相較前面的幾種方法都非常低。
最后我們來講講目前最好的單例的方法之二,這個方法是在《Effective Java》書中提到的,通過Enum來實現單例,首先我們需要了解一個大前提,Java中的Enum實質上也是一個功能齊全的類,也可以有自己的屬性和方法,而且枚舉算是單例的泛型化,本質上是單元素的枚舉,而且也可以通過Enum來實現可變的多例類型的“單例”,具體代碼如下
1 public enum EnumSingleton { 2 //定義一個枚舉的元素,就代表了Singleton的一個實例 3 instance; 4 5 private String singletonData; 6 7 public String getEnumSingleton(){ 8 return singletonData; 9 }10 11 public void setEnumSingleton(String singletonData){12 this.singletonData = singletonData;13 }14 }
也可以類似的寫上 instance2,instance3.......對于Enum來說,都是單例,這種實現形式基于JDK1.5以及JDK1.5以上
最后假設你不想使用懶加載的單例模型,你實在表示很想偷懶,那就使用餓漢式的單例吧,這種方法簡單粗暴,并且是線程安全的,就是類一旦被加載的時候就會去實例化該對象,哪怕不使用該類的時候,具體代碼如下:
1 public class EagerSingleton { 2 //直接實例化類實例,其他別無二致 3 private static EagerSingleton instance = new EagerSingleton(); 4 5 private EagerSingleton(){} 6 7 public static EagerSingleton getInstance() { 8 return instance; 9 }10 }
這種方法簡單粗暴,老少咸宜,但是性能如何就見仁見智了,
好了,差不多晚上的JAVA單例設計模式就講到這里了,最后貼上思維導圖一張,就在總綱的基礎上在Singletong的設計模式上添加的,下回我們再見,下回我們具體會講到下一個CreationPattern中的Factory Method,敬請期待。
如需轉載請告知,轉載請注明出處。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。