摘要:單例是應(yīng)用開發(fā)中一種設(shè)計(jì)模式,主要應(yīng)用場景為當(dāng)且僅當(dāng)系統(tǒng)中只能保留一個(gè)對象時(shí)使用。本文提出中可以在生產(chǎn)環(huán)境中使用的單例設(shè)計(jì)模式。在的一書中給出了三種單例設(shè)計(jì)模式采用靜態(tài)變量這種寫法使用了私有的構(gòu)造方法。
應(yīng)用場景單例是應(yīng)用開發(fā)中一種設(shè)計(jì)模式,主要應(yīng)用場景為:當(dāng)且僅當(dāng)系統(tǒng)中只能保留一個(gè)對象時(shí)使用。本文提出4中可以在生產(chǎn)環(huán)境中使用的單例設(shè)計(jì)模式。推薦使用enum的方式。
例如一下應(yīng)用場景[1]:
1、 Windows的Task Manager(任務(wù)管理器)就是很典型的單例模式(這個(gè)很熟悉吧),想想看,是不是呢,你能打開兩個(gè)windows task manager嗎?
2、網(wǎng)站的瀏覽人數(shù)統(tǒng)計(jì),一般也是采用單例模式實(shí)現(xiàn),否則難以同步。
3、應(yīng)用程序的日志應(yīng)用,一般都何用單例模式實(shí)現(xiàn),這一般是由于共享的日志文件一直處于打開狀態(tài),因?yàn)橹荒苡幸粋€(gè)實(shí)例去操作,否則內(nèi)容不好追加。
//todo
在joshua block 的《effective java second edition》 一書中給出了三種單例設(shè)計(jì)模式
public class TaskManager { public static final TaskManager INSTANCE = new TaskManager (); private TaskManager (){} //... }
這種寫法使用了私有的構(gòu)造方法。來保證只能有一個(gè)實(shí)例,但是這種方法也有例外情況,因?yàn)?,你可以通過反射來調(diào)用私有構(gòu)造方法。這個(gè)時(shí)候你可以拋出異常。以下代碼僅作為參考。
public class TaskManager { public static final TaskManager INSTANCE = new TaskManager(); private TaskManager() { if (INSTANCE != null) { try { throw new Exception("An object already exists"); } catch (Exception e) { e.printStackTrace(); } } } //... }2、采用靜態(tài)方法
public class TaskManager { private static final TaskManager INSTANCE = new TaskManager(); private TaskManager() {} public static TaskManager getINSTANCE() { return INSTANCE; } //... }3、采用enum的方式
這種模式是目前最佳的,因?yàn)椋?br>1、JVM會(huì)保證enum不能被反射并且構(gòu)造器方法只執(zhí)行一次。
2、此方法無償提供了序列化機(jī)制,絕對防止反序列化時(shí)多次實(shí)例化。
3、運(yùn)行時(shí)(compile-time )創(chuàng)建對象(懶加載) // todo 關(guān)于cmpile-time和run-time有時(shí)間我多帶帶寫一篇文章。
enum是jdk5的特性,現(xiàn)在(2017)web應(yīng)用普遍在jdk6、7、8,所以可以放心使用。
目前最佳的方式是使用接口的方式(解耦):
interface Resource { Object doSomething(); } public enum SomeThing implements Resource { INSTANCE { @Override public Object doSomething() { return "I am a Singleton nstance"; } }; } class Demo { public static void main(String[] args) { System.out.println(SomeThing.INSTANCE.doSomething()); } }
或者不使用接口的形式
public enum SomeThing { INSTANCE; public void doSomething() { System.out.println("INSTANCE = " + INSTANCE); } } class Demo { public static void main(String[] args) { SomeThing.INSTANCE.doSomething(); } }
也有人用其他的方式,我對這種方法持強(qiáng)烈反對,具體可以參考文獻(xiàn)4,以下代碼僅做參考
class Resource { } public enum SomeThing { INSTANCE; private Resource instance; SomeThing() { instance = new Resource(); } public Resource getInstance() { return instance; } } class Demo { public static void main(String[] args) { System.out.println(SomeThing.INSTANCE.getInstance()); } }
在其他文章中有提到“懶漢”、“惡漢”的名詞,其實(shí)懶漢主要就是"懶"加載[注:指在使用時(shí)裝載,不使用時(shí)不進(jìn)行裝載]
有人提出這種懶漢設(shè)計(jì)
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
很顯然這種設(shè)計(jì)線程不安全,一般不會(huì)使用。
有人又提出了懶漢改進(jìn)的方法,使其線程安全。
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
這種寫法能夠在多線程中很好的工作,而且看起來它也具備很好的lazy loading,但是,遺憾的是,因?yàn)槭侵亓考夋i,效率很低。
于是有人提出了雙重校驗(yàn)鎖機(jī)制,這個(gè)用的也比較多。
下面代碼就是用double checked locking 方法實(shí)現(xiàn)的單例,這里的getInstance()方法要檢查兩次,確保是否實(shí)例INSTANCE是否為null或者已經(jīng)實(shí)例化了,這也是為什么叫double checked locking 模式。
/** * Singleton pattern example with Double checked Locking */ public class DoubleCheckedLockingSingleton{ private volatile DoubleCheckedLockingSingleton INSTANCE; private DoubleCheckedLockingSingleton(){} public DoubleCheckedLockingSingleton getInstance(){ if(INSTANCE == null){ synchronized(DoubleCheckedLockingSingleton.class){ //double checking Singleton instance if(INSTANCE == null){ INSTANCE = new DoubleCheckedLockingSingleton(); } } } return INSTANCE; } }
參考文獻(xiàn):
[1] Jason Cai, 設(shè)計(jì)模式之——單例模式(Singleton)的常見應(yīng)用場景
[2] cantellow, 單例模式的七種寫法
[3] Javarevisited, 單例模式中為什么用枚舉更好
[4] natsumi, Java枚舉實(shí)現(xiàn)單例模式
[5] zejian_,深入理解Java枚舉類型(enum)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/70238.html
摘要:總結(jié)我們主要介紹到了以下幾種方式實(shí)現(xiàn)單例模式餓漢方式線程安全懶漢式非線程安全和關(guān)鍵字線程安全版本懶漢式雙重檢查加鎖版本枚舉方式參考設(shè)計(jì)模式中文版第二版設(shè)計(jì)模式深入理解單例模式我是一個(gè)以架構(gòu)師為年之內(nèi)目標(biāo)的小小白。 初遇設(shè)計(jì)模式在上個(gè)寒假,當(dāng)時(shí)把每個(gè)設(shè)計(jì)模式過了一遍,對設(shè)計(jì)模式有了一個(gè)最初級的了解。這個(gè)學(xué)期借了幾本設(shè)計(jì)模式的書籍看,聽了老師的設(shè)計(jì)模式課,對設(shè)計(jì)模式算是有個(gè)更進(jìn)一步的認(rèn)識。...
摘要:一基礎(chǔ)接口的意義百度規(guī)范擴(kuò)展回調(diào)抽象類的意義想不想通過一線互聯(lián)網(wǎng)公司面試文檔整理為電子書掘金簡介谷歌求職記我花了八個(gè)月準(zhǔn)備谷歌面試掘金原文鏈接翻譯者 【面試寶典】從對象深入分析 Java 中實(shí)例變量和類變量的區(qū)別 - 掘金原創(chuàng)文章,轉(zhuǎn)載請務(wù)必保留原出處為:http://www.54tianzhisheng.cn/... , 歡迎訪問我的站點(diǎn),閱讀更多有深度的文章。 實(shí)例變量 和 類變量...
摘要:在設(shè)計(jì)模式中,所有的設(shè)計(jì)模式都遵循這一原則。其實(shí)就是說在應(yīng)用程序中,所有的類如果使用或依賴于其他的類,則應(yīng)該依賴這些其他類的抽象類,而不是這些其他類的具體類。使用設(shè)計(jì)模式是為了可重用代碼讓代碼更容易被他人理解保證代碼可靠性。 這是劉意老師的JAVA基礎(chǔ)教程的筆記講的賊好,附上傳送門 傳智風(fēng)清揚(yáng)-超全面的Java基礎(chǔ) 一、面向?qū)ο笏枷朐O(shè)計(jì)原則 1.單一職責(zé)原則 其實(shí)就是開發(fā)人員經(jīng)常說的高...
摘要:關(guān)于對于重排序的講解,強(qiáng)烈推薦閱讀程曉明寫的深入理解內(nèi)存模型二重排序。語義語義單線程下,為了優(yōu)化可以對操作進(jìn)行重排序。編譯器和處理器為單個(gè)線程實(shí)現(xiàn)了語義,但對于多線程并不實(shí)現(xiàn)語義。雙重加載的單例模式分析即雙重檢查加鎖。 版權(quán)聲明:本文由吳仙杰創(chuàng)作整理,轉(zhuǎn)載請注明出處:https://segmentfault.com/a/1190000009231182 1. 引言 在開始分析雙重加鎖單...
摘要:中的詳解必修個(gè)多線程問題總結(jié)個(gè)多線程問題總結(jié)有哪些源代碼看了后讓你收獲很多,代碼思維和能力有較大的提升有哪些源代碼看了后讓你收獲很多,代碼思維和能力有較大的提升開源的運(yùn)行原理從虛擬機(jī)工作流程看運(yùn)行原理。 自己實(shí)現(xiàn)集合框架 (三): 單鏈表的實(shí)現(xiàn) 自己實(shí)現(xiàn)集合框架 (三): 單鏈表的實(shí)現(xiàn) 基于 POI 封裝 ExcelUtil 精簡的 Excel 導(dǎo)入導(dǎo)出 由于 poi 本身只是針對于 ...
閱讀 3912·2021-11-17 09:33
閱讀 1213·2021-10-09 09:44
閱讀 412·2019-08-30 13:59
閱讀 3487·2019-08-30 11:26
閱讀 2190·2019-08-29 16:56
閱讀 2862·2019-08-29 14:22
閱讀 3157·2019-08-29 12:11
閱讀 1283·2019-08-29 10:58