單例設(shè)計(jì)模式(Singleton Pattern)是最簡單且常見的設(shè)計(jì)模式之一,主要作用是提供一個(gè)全局訪問且只實(shí)例化一次的對象,避免多實(shí)例對象的情況下引起邏輯性錯(cuò)誤(實(shí)例化數(shù)量可控)…
概述
Java中,單例模式主要分三種:懶漢式單例、餓漢式單例、登記式單例三種。
特點(diǎn)
案例
在這里我推薦下我自己的JAVAqun:479121291,不管你是小白還是大牛,小編我都挺歡迎,不定期分享干貨,包括我自己整理的一份最新JAVA資料和零基礎(chǔ)入門教程!,歡迎初學(xué)和進(jìn)階中的小伙伴。
注意事項(xiàng)
解鎖姿勢
第一種:單一檢查(懶漢)非線程安全
日志
分析: 在單線程環(huán)境一切正常,balancer1和balancer2兩個(gè)對象的hashCode一模一樣,由此可以判斷出堆棧中只有一份內(nèi)容,不過該代碼塊中存在線程安全隱患,因?yàn)槿狈Ω偁帡l件,多線程環(huán)境資源競爭的時(shí)候就顯得不太樂觀了,請看上文代碼注釋內(nèi)容
第二種:無腦上鎖(懶漢)線程安全,性能較差,第一種升級(jí)版
分析: 毫無疑問,知道synchronized關(guān)鍵字的都知道,同步方法在鎖沒釋放之前,其它線程都在排隊(duì)候著呢,想不安全都不行啊,但在安全的同時(shí),性能方面就顯得短板了,我就初始化一次,你丫的每次來都上個(gè)鎖,不累的嗎(沒關(guān)系,它是為了第三種做鋪墊的)..
第三種:雙重檢查鎖(DCL),完全就是前兩種的結(jié)合體啊,有木有,只是將同步方法升級(jí)成了同步代碼塊
1.假設(shè)new LazyLoadBalancer()加載內(nèi)容過多
2.因重排而導(dǎo)致loadBalancer提前不為空
3.正好被其它線程觀察到對象非空直接返回使用
存在問題: 首先我們一定要清楚,DCL是不能保證線程安全的,稍微了解過JVM的就清楚,對比C/C++它始終缺少一個(gè)正式的內(nèi)存模型,所以為了提升性能,它還會(huì)做一次指令重排操作,這個(gè)時(shí)候就會(huì)導(dǎo)致loadBalancer提前不為空,正好被其它線程觀察到對象非空直接返回使用(但實(shí)際還有部分內(nèi)容沒加載完成)
解決方案: 用volatile修飾loadBalancer,因?yàn)関olatile修飾的成員變量可以確保多個(gè)線程都能夠順序處理,它會(huì)屏蔽JVM指令重排帶來的性能優(yōu)化。
第四種:Demand Holder (懶漢)線程安全,推薦使用
分析: 在Demand Holder中,我們在LazyLoadBalancer里增加一個(gè)靜態(tài)(static)內(nèi)部類,在該內(nèi)部類中創(chuàng)建單例對象,再將該單例對象通過getInstance()方法返回給外部使用,由于靜態(tài)單例對象沒有作為LazyLoadBalancer的成員變量直接實(shí)例化,類加載時(shí)并不會(huì)實(shí)例化LoadBalancerHolder,因此既可以實(shí)現(xiàn)延遲加載,又可以保證線程安全,不影響系統(tǒng)性能(居家旅行必備良藥?。?/p>
第五種:枚舉特性(懶漢)線程安全,推薦使用
分析: 相比上一種,該方式同樣是用到了JAVA特性:枚舉類保證只有一個(gè)實(shí)例(即使使用反射機(jī)制也無法多次實(shí)例化一個(gè)枚舉量)
第六種:餓漢單例(天生線程安全),
分析: 利用ClassLoad機(jī)制,在加載時(shí)進(jìn)行實(shí)例化,同時(shí)靜態(tài)方法只在編譯期間執(zhí)行一次初始化,也就只有一個(gè)對象。使用的時(shí)候已被初始化完畢可以直接調(diào)用,但是相比懶漢模式,它在使用的時(shí)候速度最快,但這玩意就像自己挖的坑哭著也得跳,你不用也得初始化一份在內(nèi)存中占個(gè)坑…