引言:java
這一系列文章,翻譯自網絡上的文章,不過中間會夾雜着我的的理解,非原創,不過中文應該算是原創。
數據庫
下面介紹,使用設計模式的一些好處:
設計模式
一、設計模式是已經在工業生產中使用的,用於解決特定問題的標準方法,若是你在遇到相似問題的時候,能參考相應的設計,會節省你不少的時間。
api
二、設計模式,每每能提升代碼的重用性,會減小你的開發時間。
緩存
三、既然你們都知道設計模式,包括從你的類的命名上面,類的使用上面,你們都很清楚的知道相應類的具體功能,就像每一個人都約定好的那樣。
安全
分類:
網絡
java設計模式分爲3大類:
多線程
一、對象建立
併發
二、結構
app
三、行爲
對象建立設計模式的定義:
一、在特定的問題場景中,建立設計模式會合理的根據特定的問題建立對象。由於對象的建立可能會很複雜,會增長沒必要要的複雜性,建立設計模式以不一樣的建立方法解決了這些複雜性。
對象建立設計模式包含的範圍:
一、單例
二、工廠
三、抽象工廠
四、建造者
五、原型
本節會講一下,對象建立中的單例設計模式。
單例設計模式,大部分人第一印象感受很簡單,看完這篇博客,我想你就不會這麼認爲啦,不一樣的開發者對於以何種方式實現單例設計模式是有爭議的,本文儘量的把目前實現單例的方法歸納出來,並給出最佳實戰的建議。
單例模式的定義:
單例模式是一種建立對象實例的設計模式,在建立對象的過程當中,他會保證,在一個java虛擬機裏面只有一個對象實例,同時單例設計模式,必須提供一個給外部訪問該惟一對象的入口。
單例模式的應用場景:
日誌、驅動對象、緩存、線程池等等,固然設計模式不是單一存在的,其它設計模式中也會使用單例設計模式來完成一些事情,好比:抽象工廠,建造者,原型,門面等。同時在java核心包裏面也有其身影,好比java.lang.runtime.
單例模式的實現方法的共性:
一、提供私有的構造函數,防止外部類直接初始化該對象
二、提供私有的靜態變量,固然要是該類的對象實例,惟一的對象實例。
三、public的靜態方法,這就是上面說的,提供給外部類獲取該類對象的惟一入口。
接下來,咱們會以不一樣的方法,來實現單例設計模式。
【A】飢餓實現
飢餓實現就是,等不急,當類被classloader加載的時候就初始化了該對象,這種實現有個弊端就是,當client不使用該類實例就浪費啦,不過我我的以爲,這種狀況不存在,你不用他寫他作什麼呢?代碼測試要有覆蓋率的,測試每天在你後面催着喊你把他刪掉。
public class EagerInitializedSingleton { private static final EagerInitializedSingleton instance = new EagerInitializedSingleton(); //private constructor to avoid client applications to use constructor private EagerInitializedSingleton(){} public static EagerInitializedSingleton getInstance() { return instance; } }
若是你的單例對象在建立的時候不使用過多的資源,這種方法是可行的。可是大部分的時候,單例對象建立的時候會加載不少的資源文件,好比File system,數據庫鏈接等等。咱們應該避免在客戶端調用getInstance方法以前建立這個對象,可是這種方法沒法提供異常的捕獲,異常的拋出,可能在建立對象的時候。
【B】靜態塊的實現方式
靜態塊的實現方式和上面的實現方式差很少,可是靜態塊的實現,能夠處理異常。代碼以下:
public class StaticBlockSingleton { private static StaticBlockSingleton instance; private StaticBlockSingleton(){} //static block initialization for exception handling static { try{ instance = new StaticBlockSingleton(); }catch(Exception e) { throw new RuntimeException("Exception occured in creating singleton instance"); } } public static StaticBlockSingleton getInstance() { return instance; } }
和飢餓的實現方式同樣,當類被classloader加載的時候就初始化了該對象。
【C】延遲建立
顧名思義,就是須要的時候再去建立。
public class LazyInitializedSingleton { private static LazyInitializedSingleton instance; private LazyInitializedSingleton(){} public static LazyInitializedSingleton getInstance() { if(instance == null) { instance = new LazyInitializedSingleton(); } return instance; } }
上面這種實現方法,在單線程的環境下是工做良好的,可是,在多線程併發建立對象的時候會出現問題,簡單的分析下緣由,考慮多線程併發安全的時候,首先要找到共享點,就是共享的對象,而後想象各類執行順序,你會發現instance這個地方就是共享點,當多線程執行的時候,有可能會建立多個對象,因此該方法不能保證單例實現。
【D】線程安全的建立
爲了解決多線程併發建立對象的問題,咱們引入Synchronized關鍵字,這樣的話,同一時刻,只有一個對象能訪問getInstance方法。
public class ThreadSafeSingleton { private static ThreadSafeSingleton instance; private ThreadSafeSingleton(){} public static synchronized ThreadSafeSingleton getInstance() { if(instance == null) { instance = new ThreadSafeSingleton(); } return instance; } }
這種方式是能防止多併發引發的多對象建立問題,可是寫代碼嘛,總要追求點東西,你說別人裝逼也好,賣弄也罷,總之在某一方面比你好。在多併發中,HashMap和ConcurrentHashMap,你是鎖住整個HashMap仍是HashMap的一個segment這是有質的區別的,因此改進的代碼,只朝着一個方向,那就是下降鎖的力度,因而乎,下面的代碼使用double check來下降了力度,提升了性能。
public static ThreadSafeSingleton getInstanceUsingDoubleLocking() { if(instance == null) { synchronized (ThreadSafeSingleton.class) { if(instance == null) { instance = new ThreadSafeSingleton(); } } } return instance; }
還有一種實現叫Bill Pugh,java的版本更新變化很快,包括新出的java9,可能名字不是,你們也知道,金融在將來幾年是個很火的領域,因而乎java9中集成了貨幣的api支持,這是個人猜想,哈哈,包括先在的招財寶,銅板街,挖財,聽說王福強大神,跳到挖財啦,固然我也是作金融行業的業務,有興趣的能夠留言,聽說要招人。這些題外話,可是不無用處哈,java的內存模型會致使上面的幾種方法,在不少不少線程出現的時候,會出現問題,因而乎,這我的,也就是Bill Pugh本身實現了一下這種方法:
【E】Bill Pugh實現
public class BillPughSingleton { private BillPughSingleton(){} private static class SingletonHelper { private static final BillPughSingleton INSTANCE = new BillPughSingleton(); } public static BillPughSingleton getInstance() { return SingletonHelper.INSTANCE; } }
看代碼發現Bill Pugh是經過私有靜態內部類來實現的,當單例對象被classloader加載的時候,SingletonHelper是不會被加載到內存的,除非有對象調用getInstance方法,這是最經常使用的建立單例對象的方法,由於不須要鎖,已經在多個項目中使用了這種方法,簡單有效。
【F】Enum實現
public enum EnumSingleton { INSTANCE; public static void doSomething() { //do something } }
世界上好人和壞人老是都存在的,單例對象也不例外,總有那麼幾種方法是搞破壞的,費盡千辛萬苦,建立的單例,有可能被破壞的。
【破壞者1】反射
【破壞者2】序列化
有破壞辦法,就又解決辦法,破壞者1的解決辦法是ENUM,破壞者2的解決辦法是重寫readResolve方法。這兩部分,你說是和反射有關呢,仍是和序列化有關呢,序列化都夠我寫一篇東西的了。下次再說
poke holes in the abstraction,and it starts leaking.