[英]Why is double-checked locking broken in Java?
此問題涉及舊Java版本的行為和雙重檢查鎖定算法的舊實現
較新的實現使用volatile
並依賴於稍微改變的volatile
語義,因此它們不會被破壞。
據說,除了long或double字段外,字段賦值總是原子的。
但是,當我讀到為什么雙重檢查鎖定被破壞的解釋時,它說問題在於賦值操作:
// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
}
// other functions and members...
}
- 線程A注意到該值未初始化,因此它獲得鎖定並開始初始化該值。
- 由於某些編程語言的語義,允許編譯器生成的代碼在A完成初始化之前更新共享變量以指向部分構造的對象。
- 線程B注意到共享變量已初始化(或顯示),並返回其值。 因為線程B認為該值已經初始化,所以它不會獲得鎖定。 如果B在B看到A完成的所有初始化之前使用該對象(因為A尚未完成初始化或者因為對象中的某些初始化值尚未滲透到內存B使用(緩存一致性)) ,該程序可能會崩潰。
(來自http://en.wikipedia.org/wiki/Double-checked_locking )。
什么時候可能? 是否有可能在64位JVM分配操作中不是原子的? 如果不是那么“雙重檢查鎖定”是否真的被打破了?
問題不在於原子性,而在於它的排序。 只要不違反發生之前的情況 ,JVM就可以重新排序指令以提高性能。 因此,運行時理論上可以在執行類Helper
的構造函數的所有指令之前調度更新helper
程序的指令。
引用的賦值是原子的,但構造不是! 因此,如解釋中所述,假設線程B想要在線程A完全構造它之前使用單例,它不能創建新實例,因為引用不是null,因此它只返回部分構造的對象。
如果您不確保在另一個線程加載該共享引用之前發布共享引用,則可以通過寫入其字段來重新排序對新對象的引用的寫入。 在這種情況下,另一個線程可以看到對象引用的最新值,但是對象的某些或全部狀態的過期值 - 部分構造的對象。 - Brian Goetz:Java Concurrency in Practice
由於初始檢查null未同步,因此沒有發布,並且可以進行此重新排序。
可能需要在構造函數中構造Helper
實例的幾個賦值,並且語義允許它們相對於賦值helper = new Helper()
重新排序。
因此,可以為字段helper
分配對未進行所有分配的對象的引用,以使其未完全初始化。
在java中雙重檢查鎖定有各種各樣的問題:
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
閱讀這篇文章: http : //www.javaworld.com/jw-02-2001/jw-0209-double.html即使你不理解所有細節(像我一樣),只要相信這個好方法不起作用。
對不起,這可能與這個問題有點無關,我只是好奇。 在這種情況下,在分配之前獲取鎖和/或返回值是不是更好? 喜歡:
private Lock mLock = new ReentrantLock();
private Helper mHelper = null;
private Helper getHelper() {
mLock.lock();
try {
if (mHelper == null) {
mHelper = new Helper();
}
return mHelper;
}
finally {
mLock.unlock();
}
}
或者使用雙重檢查鎖定是否有任何優勢?
/*Then the following should work.
Remember: getHelper() is usually called many times, it is BAD
to call synchronized() every time for such a trivial thing!
*/
class Foo {
private Helper helper = null;
private Boolean isHelperInstantiated;
public Helper getHelper() {
if (!isHelperInstantiated) {
synchronized(this) {
if (helper == null) {
helper = new Helper();
isHelperInstantiated = true;
}
}
}
return helper;
}
// other functions and members...
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.