Java設(shè)計(jì)模式單例模式(Singleton)用法解析
這篇文章主要介紹了Java設(shè)計(jì)模式單例模式(Singleton)用法詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
單例模式的應(yīng)用場(chǎng)景:
單例模式(Singleton Pattern)是指確保一個(gè)類在任何情況下都絕對(duì)只有一個(gè)實(shí)例。并提供一個(gè)全局反訪問點(diǎn)。單例模式是創(chuàng)建型模式。單例模式在生活中應(yīng)用也很廣泛,比如公司CEO只有一個(gè),部門經(jīng)理只有一個(gè)等。JAVA中ServletCOntext,ServetContextCOnfig等,還有spring中ApplicationContext應(yīng)用上下文對(duì)象,SessionFactory,數(shù)據(jù)庫連接池對(duì)象等。使用單例模式可以將其常駐于內(nèi)存,可以節(jié)約更多資源。
寫法:
1:懶漢模式(線程不安全)
/** * 線程不安全的懶漢式單利模式 * * Created by gan on 2019/11/17 17:33. */ public class LazySingleton { private static LazySingleton instance; //構(gòu)造方法私有化 private LazySingleton() { } public static LazySingleton getInstance() { if (instance != null) { instance = new LazySingleton(); } return instance; } }
上面的代碼,提供一個(gè)靜態(tài)對(duì)象instance,構(gòu)造函數(shù)私有化防止外部創(chuàng)建對(duì)象,提供一個(gè)靜態(tài)的getInstance方法來給訪問者一個(gè)單例對(duì)象。這種寫法的缺點(diǎn)就是沒有考慮到線程安全問題,當(dāng)多個(gè)訪問者同時(shí)訪問的時(shí)候很有可能創(chuàng)建多個(gè)對(duì)象。之所以叫懶漢式,是因?yàn)檫@種寫法是使用的時(shí)候才創(chuàng)建,起到了懶加載Lazy loading的作用,實(shí)際開發(fā)中不建議采用這種寫法。
2:線程安全的懶漢式(加鎖)
/** * 線程安全的懶漢式單利模式 * * Created by gan on 2019/11/17 17:33. */ public class LazySingleton { private static LazySingleton instance; //構(gòu)造方法私有化 private LazySingleton() { } public synchronized static LazySingleton getInstance() { if (instance != null) { instance = new LazySingleton(); } return instance; } }
這種寫法就是在第一種的基礎(chǔ)上添加了synchronized關(guān)鍵字保證了線程安全。這種寫法在并發(fā)高的時(shí)候雖然保證了線程安全,但是效率很低,高并發(fā)的時(shí)候所有訪問的線程都要排隊(duì)等待,所以實(shí)際開發(fā)中也不建議采用。
3:惡漢式(線程安全)
/** * 餓漢式(線程安全) * Created by gan on 2019/10/28 22:52. */ public class HungrySigleton { public static final HungrySigleton instance = new HungrySigleton(); private HungrySigleton(){} public static HungrySigleton getInstance(){ return instance; } }
直接在運(yùn)行(加載)這個(gè)類的時(shí)候創(chuàng)建了對(duì)象,之后直接訪問。顯然這種方式?jīng)]有起到Lazy loading的效果。但是是線程安全的,實(shí)際開發(fā)中還是比較常用。
4:靜態(tài)內(nèi)部類(線程安全)
/** * 靜態(tài)內(nèi)部類方式 * Created by gan on 2019/11/17 17:46. */ public class StaticInnerClassSingleton { //構(gòu)造方法私有化 private StaticInnerClassSingleton() {} //內(nèi)部類 private static class HolderInnerClass { //需要提供單利對(duì)象的外部類作為靜態(tài)屬性加載的時(shí)候就初始化 private static StaticInnerClassSingleton instance = new StaticInnerClassSingleton(); } //對(duì)外暴漏訪問點(diǎn) public static StaticInnerClassSingleton getInstance() { return HolderInnerClass.instance; } }
這種內(nèi)部類跟餓漢式單例有很多相似的地方,相比餓漢式單例模式的區(qū)別也是好處在于:靜態(tài)內(nèi)部類不在單例類加載時(shí)就加載,而是在調(diào)用getInstance()方法的時(shí)候才進(jìn)行加載,達(dá)到了類似于懶漢式的效果,而且這種方法又是線程安全的。實(shí)際開發(fā)中也建議采用。
5:枚舉方法單例(線程安全)
/** * 枚舉單利模式 * Created by gan on 2019/11/17 17:57. */ public enum EnumSingleton { INSTANCE; public void otherMetthod() { System.out.println("需要單利對(duì)象調(diào)用的方法。。。"); } }
Effective Java作者Josh Bloch提倡的方式,好處有如下:
1:自由串行化。
2:保證了一個(gè)實(shí)例
3:線程安全
這種方式防止了單例模式被破壞,而且簡(jiǎn)潔寫法簡(jiǎn)單,而且絕對(duì)的線程安全,但是有個(gè)缺點(diǎn)就是不能繼承。
6:雙重檢查法(通常線程安全,低概率不安全)
/** * Double check * Created by gan on 2019/11/17 18:03. */ public class DoubleCheckSingleton { private static DoubleCheckSingleton instance; private DoubleCheckSingleton() {} public static DoubleCheckSingleton getInstance() { if (instance == null) { synchronized (DoubleCheckSingleton.class) { if (instance == null) { instance = new DoubleCheckSingleton(); } } } return instance; } }
上面的這種寫法在并發(fā)極高的時(shí)候也可能會(huì)出現(xiàn)問題(當(dāng)然這種概率非常小,但是畢竟還是有的嘛),解決的方案就是給instance的聲明加上volatile關(guān)鍵字即可。于是就出現(xiàn)了下面第7總寫法。
7:Double check(volatile)
/** * Double check volatile * Created by gan on 2019/11/17 18:03. */ public class DoubleCheckSingleton { private volatile static DoubleCheckSingleton instance; private DoubleCheckSingleton() {} public static DoubleCheckSingleton getInstance() { if (instance == null) { synchronized (DoubleCheckSingleton.class) { if (instance == null) { instance = new DoubleCheckSingleton(); } } } return instance; } }
volatile關(guān)鍵字的其中一個(gè)作用就是禁止指令重排序,把instance聲明volatile后,對(duì)它的操作就會(huì)有一個(gè)內(nèi)存屏障(什么是內(nèi)存屏障?),這樣在賦值完成之前,就不會(huì)調(diào)用讀操作。這里具體的原因網(wǎng)上也是眾說紛紜,這里不進(jìn)行具體闡述。
8:ThreadLocal實(shí)現(xiàn)單例模式(線程安全)
/** * ThreadLocal實(shí)現(xiàn)單利模式 * Created by gan on 2019/11/17 18:17. */ public class ThreadLocalSingleton { private static final ThreadLocal<ThreadLocalSingleton> threadLocal = new ThreadLocal() { @Override protected ThreadLocalSingleton initialValue() { return new ThreadLocalSingleton(); } }; private ThreadLocalSingleton(){} public static ThreadLocalSingleton getInstance(){ return threadLocal.get(); } }
ThreadLocal會(huì)為每個(gè)線程提供一個(gè)獨(dú)立的變量副本,從而隔離了多個(gè)線程堆數(shù)據(jù)的訪問沖突。對(duì)于多線程資源共享問題,同步機(jī)制采用了“以時(shí)間換空間”的方式,而ThreadLocal則采用了“以空間換時(shí)間”的方式(主要就是避免了加鎖排隊(duì))。 前者提供一份變量,讓不同的線程排隊(duì)訪問,而后者為每一個(gè)線程提供了一份變量,因此可以同時(shí)訪問而互不影響。但是實(shí)際是創(chuàng)建了多個(gè)單例對(duì)象的。
單例模式的破壞:
1:序列化破壞
一個(gè)對(duì)象創(chuàng)建好以后,有時(shí)候需要將對(duì)象序列化然后寫入磁盤。下次在從磁盤中讀取并反序列化,將其轉(zhuǎn)化為內(nèi)存對(duì)象。反序列化后的對(duì)象會(huì)重新分配內(nèi)存,即創(chuàng)建型的對(duì)象。這樣就違背了單例模式的初衷。解決這種方式的方法就是在單例類中新增一個(gè) private Object readResolve();方法即可,具體原因可以看看序列化和反序列化的源碼。
2:反射
通過反射“暴力破解”也能破壞單例模式,具體暫時(shí)不闡述。
3:克隆
克隆也會(huì)破壞單例模式,具體暫時(shí)不闡述。
代碼鏈接:https://gitee.com/ganganbobo/gps-parent
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Idea中如何調(diào)出Run dashboard 或services窗口
這篇文章主要介紹了Idea中如何調(diào)出Run dashboard 或services窗口問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03解決maven中只有Lifecycle而Dependencies和Plugins消失的問題
這篇文章主要介紹了maven中只有Lifecycle而Dependencies和Plugins消失的問題及解決方法,本文通過圖文的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2020-07-07Java ArrayDeque實(shí)現(xiàn)Stack的功能
這篇文章主要介紹了Java ArrayDeque實(shí)現(xiàn)Stack功能的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-03-03Java 實(shí)現(xiàn)并發(fā)的幾種方式小結(jié)
這篇文章主要介紹了Java 實(shí)現(xiàn)并發(fā)的幾種方式小結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-05-05Java實(shí)現(xiàn)的可選擇及拖拽圖片的面板功能【基于swing組件】
這篇文章主要介紹了Java實(shí)現(xiàn)的可選擇及拖拽圖片的面板功能,涉及java基于swing組件選擇與操作圖片元素的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-01-01SpringCloud使用feign調(diào)用錯(cuò)誤的問題
這篇文章主要介紹了SpringCloud使用feign調(diào)用錯(cuò)誤的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06淺談java Iterator.remove()方法的用法(詳解)
下面小編就為大家?guī)硪黄獪\談java Iterator.remove()方法的用法(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-01-01