Java設計模式之單例模式Singleton Pattern詳解
1.單例設計模式的設計思想與應用場景
設計目的
避免因為創(chuàng)建了多個實例造成資源的浪費,且多個實例由于多次調用容易導致結果出現(xiàn)錯誤,而使用單例模式能夠保證整個應用中有且只有一個實例。
設計思想
(1)不允許其他程序用new對象: 因為new就是開辟新的空間,在這里更改數(shù)據(jù)只是更改的所創(chuàng)建的對象的數(shù)據(jù),如果可以new的話,每一次new都產(chǎn)生一個對象,這樣肯定保證不了對象的唯一性。
(2)在該類中創(chuàng)建對象: 因為不允許其他程序new對象,所以這里的對象需要在本類中new出來
(3)對外提供一個可以讓其他程序獲取該對象的方法
應用場景
一些常用的工具類、線程池、緩存,數(shù)據(jù)庫,數(shù)據(jù)庫連接池、賬戶登錄系統(tǒng)、配置文件等程序中可能只允許我們創(chuàng)建一個對象。
2.單例模式的寫法
單例模式的寫法大的方面可以分為5種五種
1.懶漢式
2.餓漢式
3.雙重校驗鎖
4.靜態(tài)內(nèi)部類
5.枚舉
2.1單例模式的餓漢式
public class Singleton { private static Singleton instance=new Singleton(); //構造器私有化,本身不可創(chuàng)建 private Singleton(){ } //定義一個獲取對象的方法 public static Singleton getInstance(){ return instance; } }
餓漢式的靜態(tài)代碼塊寫法
public class Singleton{ private static Singleton instance = null; static{ instance = new Singleton(); } private Singleton(){ } public static Singleton getInstance(){ return instance; } }
備注:兩種寫法的主要區(qū)別在于加載的時間點上,前者在于靜態(tài)常量顯示初始化之后,后者在與靜態(tài)代碼塊加載運行的時候。
優(yōu)點:餓漢式的寫法主要的優(yōu)勢在于實現(xiàn)方式簡單,在類加載的時候完成了對象的實例化,因此也可以有效避免線程問題。
缺點:可能造成資源浪費的情況,由于對象實例是在代碼加載過程中進行的,只要代碼成功加載對象實例就會被創(chuàng)建導致內(nèi)存浪費?!颈徽加玫膬?nèi)存是非常小的,所以也是推介可以使用的?!?/p>
2.2單例模式的懶漢式
2.2.1 懶漢式普通寫法
線程不安全,不推薦使用
public class Singleton { private static Singleton instance=null; private Singleton() {}; public static Singleton getInstance(){ if(instance==null){ instance=new Singleton(); } return instance; } }
與餓漢式的寫法相比較,懶漢式主要是在對**getInstance()**方法調用的時候進行實例化對象的操作,既用之再造,節(jié)省了內(nèi)存,故因此稱之為懶漢式。
懶漢式的普通寫法式存在線程安全問題的,總的來說也就是存在重復創(chuàng)建對象的風險。假設線程1進來,通過if(instance==null)的條件判斷準備運行”insatance=new Singleton()“這條代碼前,又有線程2運行,此時還沒有成功進行Singleton對象實例的創(chuàng)建,if 的條件判斷仍然成立,線程2也會去執(zhí)行instance=new Singleton()這條語句,這樣就會導致多個Singleton對象被創(chuàng)建。這就是為什么懶漢式的普通寫法會造成線程安全的原因。
2.2.2懶漢式同步代碼寫法
【給創(chuàng)建對象的語句加鎖】與2.2.1的寫法類似仍為線程不安全的寫法,不推薦使用
public class Singleton7 { private static Singleton instance=null; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { instance = new Singleton(); } } return instance; } }
與2.2.1相類似,synchronized (Singleton.class) 鎖住的只是對于對象的創(chuàng)建,仍有有可能再線程1獲得”鑰匙“還未進行對象的創(chuàng)建的時候,線程二也通過條件判斷進行鎖等待,一旦獲得“鑰匙”也會進行對象創(chuàng)建,也是會導致多對象的創(chuàng)建。
2.2.3懶漢式同步方法寫法
線程安全,但效率非常低,不推薦使用
public class Singleton { private static Singleton instance=null; private Singleton() {}; public static synchronized Singleton getInstance(){ if(instance==null){ instance=new Singleton(); } return instance; } }
缺點:效率太低了,每個線程在想獲得類的實例時候,執(zhí)行getInstance()方法都要進行同步,方法進行同步效率太低。
2.2.4單例模式懶漢式雙重校驗鎖
推介使用
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; } }
通過雙層if(insatance==null)的條件限制,防止了多線程運行就算在解鎖條件下多對象的創(chuàng)建。
通過synchronized 保證了線程安全,但再第一次對象成功創(chuàng)建后,就可以通過return instance 避免了加鎖代碼的重復運行,大大提升了效率。
在線程安全以及效率之間找到了一個平衡點。
2.3內(nèi)部類
public class Singleton{ private Singleton() {}; private static class SingletonHolder{ private static Singleton instance=new Singleton(); } public static Singleton getInstance(){ return SingletonHolder.instance; } }
這種方式跟餓漢式方式采用的機制類似,但又有不同。兩者都是采用了類裝載的機制來保證初始化實例時只有一個線程。不同
的地方在餓漢式方式是只要Singleton類被裝載就會實例化,沒有Lazy-Loading的作用,而靜態(tài)內(nèi)部類方式在Singleton類被裝載時
并不會立即實例化,而是在需要實例化時,調用getInstance方法,才會裝載SingletonHolder類,從而完成Singleton的實例化。
類的靜態(tài)屬性只會在第一次加載類的時候初始化,所以在這里,JVM幫助我們保證了線程的安全性,在類進行初始化時,別的線程是
無法進入的。
優(yōu)點:避免了線程不安全,延遲加載,效率高。
2.4枚舉
public enum SingletonEnum { instance; private SingletonEnum() {} public void method(){ } }
也就是直接將枚舉類Enum直接當作一個上述例子中的Singleton所對應的實例去進行使用,可以直接通過SingletonEnum.instance.method();的方式對實例中我們要使用的方法進行調用。
不僅能避免多線程同步問題,而且還能防止反序列化重新創(chuàng)建新的對象??赡苁且驗槊杜e在JDK1.5中才添加,所以在實際項目開發(fā)中,很少見人這么寫過,這種方式也是最好的一種方式,如果在開發(fā)中JDK滿足要求的情況下建議使用這種方式。
總結
對于寫法的選擇,開源項目也是采用的單例模式懶漢式雙重校驗鎖這種寫法,其實最安全的寫法是枚舉,它的實現(xiàn)非常簡單而且最安全可謂很完美,但是可能是因為支持的JDK版本有限又或者是因為枚舉大家不熟悉所以目前使用的人并不多,可以更多的進行嘗試。
另外當我們使用反射機制時可能不能保證實例的唯一性,但是枚舉始終可以保證唯一性【這與枚舉類在初始類加載的過程以及進行的操作有關】。
到此這篇關于Java設計模式之單例模式Singleton Pattern詳解的文章就介紹到這了,更多相關Java單例模式Singleton Pattern內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Windows下使用Graalvm將Springboot應用編譯成exe大大提高啟動和運行效率(推薦)
這篇文章主要介紹了Windows下使用Graalvm將Springboot應用編譯成exe大大提高啟動和運行效率,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02springboot項目中的bootstrap.yml配置不生效的原因及解決(沒有自動提示)
新創(chuàng)建一個 springboot項目,添加了 bootstrap.yml 文件,發(fā)現(xiàn)文件并沒有如預期變成綠色葉子,編寫的時候也沒有自動提示,啟動的時候,發(fā)現(xiàn)端口是8080,由此發(fā)現(xiàn)配置并沒有生效,所以本文給大家講解了springboot項目中的bootstrap.yml配置不生效的原因及解決2024-01-01基于Spring AOP proxyTargetClass的行為表現(xiàn)總結
這篇文章主要介紹了Spring AOP proxyTargetClass的行為表現(xiàn)總結,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08