摘要:非線(xiàn)程安全的雙重檢查鎖這里看起來(lái)很完美,但是是一個(gè)錯(cuò)誤的優(yōu)化,代碼在讀取到不為的時(shí)候,引用的對(duì)象有可能換沒(méi)有完成初始化,這樣返回的是有問(wèn)題的。
在Java多線(xiàn)程程序中,有時(shí)需要采用延遲初始化來(lái)降低初始化類(lèi)和創(chuàng)建對(duì)象的開(kāi)銷(xiāo),雙重檢查鎖定是常見(jiàn)的延遲初始化技術(shù),但它是一種錯(cuò)誤的用法
使用syncronized實(shí)現(xiàn)
public synchronized static Instance getInstance() { if (instance == null) { instance = new Instance(); } return instance; }
多線(xiàn)程情況下性能開(kāi)銷(xiāo)比較大。
非線(xiàn)程安全的雙重檢查鎖
public static Instance getInstance2() { if (instance2 == null) { synchronized (UnsafeLazyInitialization.class) { if (instance2 == null) { instance2 = new Instance(); } } } return instance2; }
這里看起來(lái)很完美,但是是一個(gè)錯(cuò)誤的優(yōu)化,代碼在讀取到instance2不為null的時(shí)候,instance引用的對(duì)象有可能換沒(méi)有完成初始化,這樣返回的instance2是有問(wèn)題的。
出現(xiàn)這個(gè)問(wèn)題的根源在什么地方?
instance2 = new Instance();這一行代碼在處理器執(zhí)行的時(shí)候有三部操作:
1、memory = allocate() //分配內(nèi)存
2、ctorInstance(memory) //初始化對(duì)象
3、instance = memory //設(shè)置instance指向剛剛分配的內(nèi)存地址
上面的三行代碼中,2和3之間可能會(huì)被指令重排序。如果重排序之后的順序?yàn)?,3,2.線(xiàn)程A執(zhí)行2的時(shí)候,線(xiàn)程A判斷instance2不為空,返回的instance2對(duì)象就是一個(gè)還未初始化的對(duì)象。
所以對(duì)于上面的解決思路有兩種:
1、不允許2和3進(jìn)行指令的重排序
3、允許2和3重排序,但是不允許其他線(xiàn)程看到這個(gè)重排序。
不允許2和3進(jìn)行指令重排序(線(xiàn)程安全的雙重檢查鎖)
/**聲明為volatile之后,2和3的指令重排序會(huì)被禁止*/ public static volatile Instance instance3; public static Instance getInstance3() { if (instance3 == null) { synchronized (UnsafeLazyInitialization.class) { if (instance3 == null) { instance3 = new Instance(); } } } return instance3; }
-基于類(lèi)初始化的解決
public class InstanceFactory { private static class InstanceHolder { public static Instance instance = new Instance(); } public static Instance getInstance () { return InstanceHolder.instance; } }
這個(gè)是基于JVM的特性:JVM在類(lèi)初始化的時(shí)候,會(huì)執(zhí)行類(lèi)初始化,在執(zhí)行類(lèi)初始化期間,JVM會(huì)獲取一把鎖,這個(gè)鎖可以同步多個(gè)線(xiàn)程對(duì)同一個(gè)類(lèi)的初始化。初始化一個(gè)類(lèi),包括執(zhí)行這個(gè)類(lèi)的靜態(tài)初始化和初始化這個(gè)類(lèi)中的靜態(tài)字段。根據(jù)Java語(yǔ)言規(guī)范,在首次發(fā)生下面的任何一種情況,一個(gè)類(lèi)或接口類(lèi)型將立即被初始化。
1)T的實(shí)例類(lèi)型被創(chuàng)建
2)T是一個(gè)類(lèi), 且T中的靜態(tài)方法被調(diào)用
3)T聲明的一個(gè)靜態(tài)字段被賦值
4)T聲明的靜態(tài)字段被使用
在這里,首次執(zhí)行g(shù)etInstance(),那么InstanceHolder會(huì)進(jìn)行初始化。
任何的線(xiàn)程安全操作在底層都是對(duì)應(yīng)指令重排序以及內(nèi)存可見(jiàn)性的問(wèn)題。操作系統(tǒng)才是根本啊~~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74943.html
摘要:基于的雙重檢查鎖定的解決方案對(duì)于前面的基于雙重檢查鎖定來(lái)實(shí)現(xiàn)延遲初始化的方案指示例代碼,我們只需要做一點(diǎn)小的修改把聲明為型,就可以實(shí)現(xiàn)線(xiàn)程安全的延遲初始化。 雙重檢查鎖定的由來(lái) 在java程序中,有時(shí)候可能需要推遲一些高開(kāi)銷(xiāo)的對(duì)象初始化操作,并且只有在使用這些對(duì)象時(shí)才進(jìn)行初始化。此時(shí)程序員可能會(huì)采用延遲初始化。但要正確實(shí)現(xiàn)線(xiàn)程安全的延遲初始化需要一些技巧,否則很容易出現(xiàn)問(wèn)題。比如,下...
摘要:注意,禁止指令重排序在之后才被修復(fù)使用局部變量?jī)?yōu)化性能重新查看中雙重檢查鎖定代碼。幫助文檔雙重檢查鎖定與延遲初始化有關(guān)雙重檢查鎖定失效的說(shuō)明 雙重檢查鎖定(Double check locked)模式經(jīng)常會(huì)出現(xiàn)在一些框架源碼中,目的是為了延遲初始化變量。這個(gè)模式還可以用來(lái)創(chuàng)建單例。下面來(lái)看一個(gè) Spring 中雙重檢查鎖定的例子。 showImg(https://segmentfaul...
摘要:如果是后者,則在執(zhí)行完畢未執(zhí)行之前,被線(xiàn)程二搶占了,這時(shí)已經(jīng)是非了但卻沒(méi)有初始化,所以線(xiàn)程二會(huì)直接返回在之后雙重檢查鎖定才能夠正常達(dá)到單例效果,之前有個(gè)坑。所以,在版本前,雙重檢查鎖形式的單例模式是無(wú)法保證線(xiàn)程安全的。 第一種(懶漢, 線(xiàn)程不安全): public class Singleton { private static Singleton instance; ...
摘要:雙重檢查鎖定以下稱(chēng)為已被廣泛當(dāng)做多線(xiàn)程環(huán)境下延遲初始化的一種高效手段。由于沒(méi)有對(duì)這些做出明確規(guī)定,很難說(shuō)是否有效??梢栽谥惺褂蔑@式的內(nèi)存屏障來(lái)使生效,但中并沒(méi)有這些屏障。如果改變鎖釋放的語(yǔ)義釋放時(shí)執(zhí)行一個(gè)雙向的內(nèi)存屏障將會(huì)帶來(lái)性能損失。 雙重檢查鎖定(以下稱(chēng)為DCL)已被廣泛當(dāng)做多線(xiàn)程環(huán)境下延遲初始化的一種高效手段。 showImg(http://segmentfault.com/i...
摘要:對(duì)于而言,它執(zhí)行的是一個(gè)個(gè)指令。在指令中創(chuàng)建對(duì)象和賦值操作是分開(kāi)進(jìn)行的,也就是說(shuō)語(yǔ)句是分兩步執(zhí)行的。此時(shí)線(xiàn)程打算使用實(shí)例,卻發(fā)現(xiàn)它沒(méi)有被初始化,于是錯(cuò)誤發(fā)生了。 1.餓漢式單例 public class Singleton { private static Singleton instance = new Singleton(); ...
閱讀 1024·2021-11-24 10:30
閱讀 2345·2021-10-08 10:04
閱讀 3999·2021-09-30 09:47
閱讀 1474·2021-09-29 09:45
閱讀 1469·2021-09-24 10:33
閱讀 6310·2021-09-22 15:57
閱讀 2376·2021-09-22 15:50
閱讀 4110·2021-08-30 09:45