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