JAVA單例之我見

為什么80%的碼農都做不了架構師?>>> ??hot3.png

?????? 單例模式作為設計模式中最簡單的一種,是一個被說爛了的東西。但是在項目中還是會發現關于單例模式的一些錯誤實現,可見單例也并不是我們想象的那么簡單。最近陸陸續續看了幾篇關于單例的博客,很受啟發,所以覺得有必要總結一下(只涉及常用的雙重檢查鎖定、靜態內部類、枚舉三種單例實現方法),本文將從安全和性能兩個方面闡述我對單例模式三種最佳實踐的理解。不當之處,請 指正。

1、雙重校驗鎖定

?

//Double Checked locking
public class SingletonByDCL
{private Map<Integer, String> configMap;private volatile static SingletonByDCL instance = null;private SingletonByDCL(Map<Integer, String> configMap){this.configMap = configMap;}public static SingletonByDCL getInstance(){SingletonByDCL inst = instance;if (null == inst){synchronized (SingletonByDCL.class){inst = instance;if (null == inst){inst = new SingletonByDCL(ConfigReader.configMap);instance = inst;}}}return inst;}
}

?

通過synchronized和volatile實現了線程安全。其中需要注意的是實例變量一定要用volatile修飾。原因可參考Java 單例真的寫對了么?。當單例對象需要被序列化時,就應該考慮單例實現的序列化安全,在Singleton中定義readResolve方法,并在該方法中指定要返回的對象的生成策略,就可以方式單例被破壞,原理可參考單例與序列化的那些事兒。可能會有人使用反射強行調用我們的私有構造器,為了保證訪問安全,可以修改構造器,讓它在創建第二個實例的時候拋異常。

2、靜態內部類

?

public class SingletonByInnerStaticClass
{private Map<Integer, String> configMap;private SingletonByInnerStaticClass(Map<Integer, String> configMap){this.configMap = configMap;}private static class SingletonHolder{private static SingletonByInnerStaticClass instance = new SingletonByInnerStaticClass(ConfigReader.configMap);}public static SingletonByInnerStaticClass getInstance(){return SingletonHolder.instance;}
}

?

線程安全,這是 Java 運行環境自動給保證的,在加載的時候,會自動隱形的同步。在訪問對象的時候,不需要同步 Java 虛擬機又會自動取消同步。對于序列化安全和訪問安全的保證,解決方法同“雙重檢查鎖定”。

3、枚舉

?

public enum SingletonByEnum
{instanse(ConfigReader.configMap);private Map<Integer,String> configMap;private SingletonByEnum(Map<Integer,String> configMap){this.configMap = configMap;}public Map<Integer, String> getConfigMap(){return configMap;}
}

當一個Java類第一次被真正使用到的時候靜態資源被初始化、Java類的加載和初始化過程都是線程安全的。所以,創建一個enum類型(可用javap查看enum編譯后的class文件,從而了解enum包含哪些靜態資源)是線程安全的。為了保證枚舉類型像Java規范中所說的那樣,每一個枚舉類型極其定義的枚舉變量在JVM中都是唯一的,在枚舉類型的序列化和反序列化上,Java做了特殊的規定,從而保證序列化安全。

?

private static void testSingletonByEnum() throws IOException, FileNotFoundException, ClassNotFoundException{// 序列化ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempfile"));oos.writeObject(SingletonByEnum.instanse);// 反序列化ObjectInputStream ois = new ObjectInputStream(new FileInputStream("tempfile"));SingletonByEnum s = (SingletonByEnum) ois.readObject();// 判斷是否為同一對象if (s == SingletonByEnum.instanse){System.out.println("創建的是同一個實例");} else{System.out.println("創建的不是同一個實例");}}

?

創建的是同一個實例

?

?

?

同時java.lang.reflect.Constructor的newInstance()方法中有如下代碼,禁止了通過反射構造枚舉對象,所以枚舉可以保證訪問安全。關于枚舉如何保證線程安全和序列化安全,可參考深度分析 Java 的枚舉類型:枚舉的線程安全性及序列化問題

if ((clazz.getModifiers() & Modifier.ENUM) != 0)   throw new IllegalArgumentException("Cannot reflectively create enum objects");

4、三種方法的性能比較

三種方法都實現了延遲加載,在8線程同時調用,每個線程調用100000000次的情況下時間對比如下:

SingletonByDCL—》845

SingletonByEnum—》90

SingletonByInnerStaticClass—》89

具體測試代碼參見附件工程中的com.zjg.perf.PerformanceTest2

綜上所述,Effective Java中推薦使用的枚舉實現單例無論從安全還是性能都是有道理的。當然代碼沒有一勞永逸的寫法,只有在特定條件下最合適的寫法。在不同的平臺、不同的開發環境(尤其是jdk版本)下,也就會有不同的最優解。比如枚舉在Android平臺上卻是不被推薦的。在這篇Android Training中明確指出:

Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.

再比如雙重檢查鎖法,不能在jdk1.5之前使用,而在Android平臺上使用就比較放心了(一般Android都是jdk1.6以上了,不僅修正了volatile的語義問題,還加入了不少鎖優化,使得多線程同步的開銷降低不少)。

參考文章:

http://blog.jobbole.com/94074/? 深度分析 Java 的枚舉類型:枚舉的線程安全性及序列化問題

http://www.importnew.com/18872.html? 你真的會寫單例模式嗎——Java實現

http://www.hollischuang.com/archives/1144? 單例與序列化的那些事兒

http://www.race604.com/java-double-checked-singleton/?utm_source=tuicool&utm_medium=referral Java 單例真的寫對了么?

demo工程:https://git.oschina.net/zjg23/SingletonDemo

轉載于:https://my.oschina.net/zjg23/blog/671445

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284816.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284816.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284816.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

「劉一哥GIS」系列專欄《QGIS入門實戰精品教程(配套案例數據)》

「劉一哥GIS」系列專欄《QGIS入門實戰精品教程&#xff08;配套案例數據&#xff09;》全新上線了&#xff0c;歡迎廣大GISer朋友關注&#xff0c;一起探索GIS奧秘&#xff0c;分享GIS價值&#xff01; 本專欄以實戰案例的形式&#xff0c;深入淺出地介紹了QGIS的基本使用方法&…

MyEclipse的Git配置

1.下載&#xff1a;git的插件egit 并解壓 插件 下載地址&#xff1a;http://www.eclipse.org/egit/download/ 所有版本&#xff1a;http://wiki.eclipse.org/EGit/FAQ#Where_can_I_find_older_releases_of_EGit.3F 2.在MyEclipse安裝目錄下的dropins文件夾下創建egit文件夾 3.…

ASP.NET Core 定時刷新第三方 Token

前言在《ASP.NET Core 自動刷新JWT Token》中我們實現了為客戶端自動提供最新的Token。但是&#xff0c;當我們的 ASP.NET Core 應用需要調用第三方 API 時&#xff0c;如何更優雅地刷新第三方提供的 Token 呢&#xff1f;思路我們可以在啟動時就獲取第三方提供的 Token 并保存…

VSCode 插件開發實例(WebView):微信讀書 ^-^邊擼代碼邊看小說^-^

最終效果 主要代碼 package.json {"name": "WeReadForVSCodeJackieZheng","repository": {"type": "git","url": "https://github.com/JackieZheng/WeReadForVSCode.git"},"displayName": &q…

【QGIS入門實戰精品教程】4.7:QGIS如何將矢量數據轉為GeoJSON格式?

本文以案例的形式,講述在QGIS專業軟件中,將矢量數據轉為GeoJSON的方法。 擴展閱讀: 【ArcGIS風暴】如何將矢量數據(點、線、面)折點坐標轉為GeoJSON格式? 在QGIS中,可以直接將數據導出為GeoJSON格式。具體操作方法是:右鍵矢量數據圖層→導出圖層→到文件,如下圖所示:…

Git的純命令操作,Install,Clone , Commit,Push,Pull,版本回退,撤銷更新,分支的創建/切換/更新/提交/合并,代碼沖突...

Git的純命令操作&#xff0c;Install&#xff0c;Clone , Commit&#xff0c;Push&#xff0c;Pull&#xff0c;版本回退&#xff0c;撤銷更新&#xff0c;分支的創建/切換/更新/提交/合并&#xff0c;代碼沖突 這篇是接著上篇分布式版本庫——Windows下Git的環境部署以及在Git…

掌握jQuery插件開發

在實際開發工作中&#xff0c;總會碰到像滾動&#xff0c;分頁&#xff0c;日歷等展示效果的業務需求&#xff0c;對于接觸過jQuery以及熟悉jQuery使用的人來說&#xff0c;首先想到的肯定是尋找現有的jQuery插件來滿足相應的展示需求。目前頁面中常用的一些組件&#xff0c;都…

List 分頁加載數據控制機制

分頁加載是一種應用很廣泛的數據展示控制機制&#xff0c;相信絕大多數開發者對于這一套機制都非常熟悉。這篇文章的主要目的結合實際的使用場景&#xff0c;對以往在開發中遇到一些概念進行梳理&#xff0c;歸納的同時加深理解&#xff0c;也希望能幫助更多剛剛接觸到開發的同…

【QGIS入門實戰精品教程】3.3:QGIS如何打開ArcGIS創建的文件數據庫(GDB)?

在行業應用中,GIS地理空間數據往往存儲在ESRI ArcGIS的文件地理數據庫(File GeodataBase),因此,ArcGIS與QGIS的數據交互、共享就顯得非常重要。QGIS3可以直接打開File GDB數據,并對數據進行顯示、查看、處理等操作。具體的步驟如下: 相關閱讀:【QGIS入門實戰精品教程】3…

.NET 6 Linux 系統服務 Systemd (案例版)

.Net Core Windows 系統服務&#xff0c;之前已經寫過了&#xff0c;但是&#xff0c;對于Linux的系統服務&#xff0c;卻沒有寫過&#xff0c;主要是因為&#xff0c;大部分Linux的.Net服務都直接Docker了&#xff0c;當然&#xff0c;不排除有這種使用的&#xff0c;可能搞成…

封裝一個Array 數據 綁定 html select 方法

/** * * param {} select select控件 * param {} nodes Array數據 * param {} value value綁定項 * param {} text text綁定項 * param {} prompt 選擇提示信息 * returns {} */ function bindSelect(select, nodes, value, text, prompt) {select.empty();if (prom…

XmlParser和HtmlParser

經常要用的Xml和Html解決&#xff0c;實際上這個領域也有非常好的解決方案。 相對來說現在各種開源的Xml解析功能比較豐富&#xff0c;機制也比較靈活&#xff0c;但是由于他功能比較完善&#xff0c;干的事情比較多&#xff0c;所以性能方面也慢一點&#xff1b;另外&#xff…

【QGIS入門實戰精品教程】3.2:QGIS如何打開ArcGIS創建的個人數據庫(MDB)?

文章目錄 1. 下載并安裝Microsoft Access 2010數據庫引擎2. 配置系統環境變量3. 打開mdb數據庫【相關閱讀】 【QGIS入門實戰精品教程】005:QGIS如何打開ArcGIS創建的文件數據庫(GDB)? 1. 下載并安裝Microsoft Access 2010數據庫引擎 https://www.microsoft.com/zh-cn/down…

SharedPreferences操作數據

SharedPreferences是一個簡單輕量的android存儲數據類。適合保存配置信息等。是以XML方式存儲的。 存儲數據 SharedPreferences sharedPreferencesgetSharedPreferences("test", Context.MODE_PRIVATE);sharedPreferences.edit().putString("userName",&qu…

[轉]IaaS、PaaS、SaaS、CaaS、MaaS五者的區別

云計算構架圖 很明顯&#xff0c;這五者之間主要的區別在于第一個單詞&#xff0c;而aaS都是as-a-service&#xff08;即服務&#xff09;的意思&#xff0c;這五個模式都是近年來興起的&#xff0c;且這五者都是云計算的落地產品&#xff0c;所以我們先來了解一下云…

使用java開發簡單的mis系統所需的技術

開發mis系統用到的技術 1. b/s架構&#xff1a;就broser/server&#xff0c;瀏覽器/服務器的說法。服務器端要運行tomcat&#xff0c;提供鏈接數據庫服務供java代碼讀寫數據&#xff0c;這個可以在eclipse中配置運行。瀏覽器則解釋jsp或html格式中的標記元素&#xff0c;用于顯…

聊聊 C++ 中的四種類型轉換符

一&#xff1a;背景 在玩 C 的時候&#xff0c;經常會用 void* 來指向一段內存地址開端&#xff0c;然后再將其強轉成尺度更小的 char* 或 int* 來丈量一段內存&#xff0c;參考如下代碼&#xff1a;int main() {void* ptr malloc(sizeof(int) * 10);int* int_ptr (int*)ptr;…

【QGIS入門實戰精品教程】3.1:QGIS如何連接SQL Server數據庫?

文章目錄 軟件必備測試SQL Server服務是否啟動QGIS與SQL Server建立連接軟件必備 SQL Server 2008 R2QGIS 3.22.3測試SQL Server服務是否啟動 打開SQL Server配置管理器,查看MSSQLSERVER服務的啟動情況。

開源重量級的流程引擎或UI引擎

關注這兩個方面的同學個踴躍加粉了~~同時在下面回復期望開源哪一個&#xff0c;將先開源呼聲高的一個。 好吧&#xff0c;先小秀一下UI&#xff1a; 增加下面的pom依賴&#xff0c;表示此工程需要org.tinygroup.aerowindow ?12345<dependency> <groupId>org.tinyg…

阿里云網盤內測開啟 填寫申請表獲取邀請碼 附最新申請地址

日前有相關報道顯示&#xff0c;阿里方面推出了一款名為“阿里云網盤”的獨立App&#xff0c;并且其是由阿里云團隊開發&#xff0c;定位則是為C端用戶提供可靠安全的存儲備份及智能相冊等功能。據了解&#xff0c;目前這款APP尚未正式上線&#xff0c;暫時還在邀請制測試階段。…