摘要:前言文章介紹了單例模式五種實現的方式,分別是懶漢,餓漢,靜態內部類,雙重檢驗鎖以及枚舉實現方式,并主要關心加載時機以及線程安全。
前言
文章介紹了單例模式五種實現的方式,分別是懶漢,餓漢,靜態內部類,雙重檢驗鎖以及枚舉實現方式,并主要關心加載時機以及線程安全。首先,通俗點講,餓漢就是這個類還沒被使用到的時候,實例已經創建好了;而懶漢是使用到的時候才創建對應的實例。線程安全方面主要考慮實例化時候是否確保一個實例,對于單例類中其他方法的線程安全不予考慮。
懶漢模式先來一個最直觀的代碼:
public class Singleton { private static Singleton instance = null; private Singleton(){} public static Singleton getInstance(){ //如果還沒有被實例化過,就實例化一個,然后返回 if(instance == null){ instance = new Singleton(); } return instance; } }
這段代碼里,我們沒有考慮線程安全,所以可能就會產生多個實例,但是這個例子能很好的表達單例模式的思想,就是保持只有一個實例,先理解了基礎,我們再一步步發展。
所以線程安全的事情還是得解決啊~于是有了以下的代碼:
public class Singleton { private static Singleton instance = null; private Singleton(){} public static synchronized Singleton getInstance(){ //如果還沒有被實例化過,就實例化一個,然后返回 if(instance == null){ instance = new Singleton(); } return instance; } }
這段代碼只加了一個關鍵字synchronized用于確保getInstance方法線程安全,但是這種方式問題很大啊,畢竟所有的線程到了這個方法全得排隊等著,對性能的損耗非常大,不過沒關系,我們這里著重先解決掉線程安全的問題,接下來會有辦法解決這個效率低下的問題(如果你著急那就直接去看雙重校驗鎖吧...)
懶漢模式將實例化的時機放到了需要使用的時候(餓漢是類加載了就有實例),也就是“延遲加載”,相比餓漢,能避免了在加載的時候實例化有可能用不到的實例,但是問題也很明顯,我們要花精力去解決線程安全的問題。
餓漢模式餓漢模式相比懶漢模式,在類加載的時候就已經存在一個實例,舉個例子,比如數據庫連接吧,懶漢就是第一次訪問數據庫的時候我才去創建一個連接,而餓漢呢,是你程序啟動了,類加載好了的時候,我已經有個連接了,你用不用不一定了,所以餓漢的缺點也就出來了:可能會產生很多無用的實例。
public class Singleton { //類加載的時候instance就已經指向了一個實例 private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }
那么加載時機的問題我們已經說過了,接下來就是線程安全了,代碼里我們并沒有看見synchronized關鍵字,那么這種方式是如何確保線程安全的呢,這個就是JVM類加載的特性了,JVM在加載類的時候,是單線程的,所以可以保證只存在單一的實例。
雙重校驗鎖首先要說明的是,雙重檢驗鎖也是一種延遲加載,并且較好的解決了在確保線程安全的時候效率低下的問題。
public class Singleton { private static Singleton instance = null; private Singleton(){} public static Singleton getInstance(){ if(instance == null){ synchronized (Singleton.class){ if(instance == null){ instance = new Singleton(); } } } return instance; } }
對比一下最原始的那種線程安全的方法(就是懶漢模式的第二種代碼),那種方法將整個getInstance方法鎖住,那么每次調用那個方法都要獲得鎖,釋放鎖,等待等等...而雙重校驗鎖鎖住了部分的代碼。進入方法如果檢查為空才進入同步代碼塊,這樣很明顯效率高了很多。
有人疑問為什么instance==null要判斷兩次嗎,那我們先去掉第二次的判斷。
如果兩個線程一起調用getInstance方法,并且都通過了第一次的判斷instance==null,那么第一個線程獲取了鎖,然后實例化了instance,然后釋放了鎖,然后第二個線程得到了線程,然后馬上也實例化了instance,這就尷尬了。單例模式就失敗了。
所以加上第二次判斷后,先進來的線程判斷了一下,哦,為空,我創建一個,然后創建一個實例之后釋放了鎖,第二個線程進來之后,哎?已經有了,那我就不用創建了,然后釋放了鎖,開開心心的完成了單例模式。
懶漢模式需要考慮線程安全,所以我們多寫了好多的代碼,餓漢模式利用了類加載的特性為我們省去了線程安全的考慮,那么,既能享受類加載確保線程安全帶來的便利,又能延遲加載的方式,就是靜態內部類。Java靜態內部類的特性是,加載的時候不會加載內部靜態類,使用的時候才會進行加載。而使用到的時候類加載又是線程安全的,這就完美的達到了我們的預期效果~
public class Singleton { private static class SingletonHolder{ private static Singleton instance = new Singleton(); } private Singleton(){} public static Singleton getInstance(){ return SingletonHolder.instance; } }枚舉
JDK1.5提供了一個新的數據類型,枚舉。枚舉的出現提供了一個較為優雅的方式取代以前大量的static final類型的變量。而這里,我們也利用枚舉的特性,實現了單例模式,這種思路是《Effective Java》中第三條最后一段給出的實現方式,有興趣的可以看看這本書~
代碼簡單到無法理解:
public enum Singleton { INSTANCE; }
外部調用由原來的Singleton.getInstance變成了Singleton.INSTANCE了。
這里要注意,原來的class已經換成了關鍵字enum,但是其實無所謂的,看下繼承關系就能知道,其實還是一個class
而且我們可以查看Enum的源碼:
這里能看到實現了Serializable接口,所以不用考慮序列化的問題(其實序列化反序列化也能導致單例失敗的,但是我們這里不過多研究)。對于線程安全,同樣的,加載的時候JVM能確保只加載一個實例。
總結在單例模式各種設計的方法中,我們使用到了內部靜態類的特性,使用了枚舉的特性,所以基礎非常重要,單例模式是設計模式之一,而設計模式其實是對語言特性不足的一面進一步的包裝。吸納基礎,工作學習多加思考,設計模式也就自然而然的能夠理解。
另外,好多帖子都說利用枚舉的方式在團隊合作中不常使用,因為需要配合,用了枚舉別人不熟悉。這點我是不同意的,如果因為大家不懂不熟悉而放棄了使用很棒的特性,那么就永遠抱著舊的方式停滯不前,JDK的更新也失去了意義。
還是希望能夠不斷的接觸Java新鮮的想法,在深厚的基礎上迸發不一樣的思路,然后去解決實際的問題。
以上,如有不妥,還望指正。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70194.html
摘要:總體來說設計模式分為三大類創建型模式共五種工廠方法模式抽象工廠模式單例模式建造者模式原型模式。優點一實例控制單例模式會阻止其他對象實例化其自己的單例對象的副本,從而確保所有對象都訪問唯一實例。 總體來說設計模式分為三大類: 創建型模式---共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。 結構型模式---共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式...
摘要:懶漢非線程安全,需要用一定的風騷操作控制,裝逼失敗有可能導致看一周的海綿寶寶餓漢天生線程安全,的時候就已經實例化好,該操作過于風騷會造成資源浪費單例注冊表初始化的時候,默認單例用的就是該方式特點私有構造方法,只能有一個實例。 單例設計模式(Singleton Pattern)是最簡單且常見的設計模式之一,主要作用是提供一個全局訪問且只實例化一次的對象,避免多實例對象的情況下引起邏輯性錯...
摘要:創建型模式主要有以下五種簡單工廠模式和工廠方法模式抽象工廠模式單例模式建造者模式原型模式在設計模式一書中將工廠模式分為兩類工廠方法模式與抽象工廠模式。 一、 設計模式(Design pattern)是什么 設計模式是一套被反復使用、多數人知曉、經過分類編目的代碼設計的經驗總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 二、 為什么會有設計模式 在軟件開發過...
閱讀 1156·2021-11-24 09:38
閱讀 3604·2021-11-22 15:32
閱讀 3458·2019-08-30 15:54
閱讀 2568·2019-08-30 15:53
閱讀 1494·2019-08-30 15:52
閱讀 2497·2019-08-30 13:15
閱讀 1837·2019-08-29 12:21
閱讀 1395·2019-08-26 18:36