天天看點

Java記憶體模型FAQ(十一)新的記憶體模型是否修複了雙重鎖檢查問題?原文Does the new memory model fix the “double-checked locking” problem?

譯者:alex

臭名昭著的雙重鎖檢查(也叫多線程單例模式)是一個騙人的把戲,它用來支援lazy初始化,同時避免過度使用同步。在非常早的jvm中,同步非常慢,開發人員非常希望删掉它。雙重鎖檢查代碼如下:

許多人認為使用volatile關鍵字能夠消除雙重鎖檢查模式的問題。在1.5的jvm之前,volatile并不能保證這段代碼能夠正常工作(因環境而定)。在新的記憶體模型下,執行個體字段使用volatile可以解決雙重鎖檢查的問題,因為在構造線程來初始化一些東西和讀取線程傳回它的值之間有happens-before關系。

然後,對于喜歡使用雙重鎖檢查的人來說(我們真的希望沒有人這樣做),仍然不是好消息。雙重鎖檢查的重點是為了避免過度使用同步導緻性能問題。從java1.0開始,不僅同步會有昂貴的性能開銷,而且在新的記憶體模型下,使用volatile的性能開銷也有所上升,幾乎達到了和同步一樣的性能開銷。是以,使用雙重鎖檢查來實作單例模式仍然不是一個好的選擇。(修訂—在大多數平台下,volatile性能開銷還是比較低的)。

使用iodh來實作多線程模式下的單例會更易讀:

這段代碼是正确的,因為初始化是由static字段來保證的。如果一個字段設定在static初始化中,對其他通路這個類的線程來說是是能正确的保證它的可見性的。

<a></a>

the (infamous) double-checked locking idiom (also called the multithreaded singleton pattern) is a trick designed to support lazy initialization while avoiding the overhead of synchronization. in very early jvms, synchronization was slow, and developers were eager to remove it — perhaps too eager. the double-checked locking idiom looks like this:

many people assumed that the use of the volatile keyword would eliminate the problems that arise when trying to use the double-checked-locking pattern. in jvms prior to 1.5, volatile would not ensure that it worked (your mileage may vary). under the new memory model, making the instance field volatile will “fix” the problems with double-checked locking, because then there will be a happens-before relationship between the initialization of the something by the constructing thread and the return of its value by the thread that reads it.

however, for fans of double-checked locking (and we really hope there are none left), the news is still not good. the whole point of double-checked locking was to avoid the performance overhead of synchronization. not only has brief synchronization gotten a lot less expensive since the java 1.0 days, but under the new memory model, the performance cost of using volatile goes up, almost to the level of the cost of synchronization. so there’s still no good reason to use double-checked-locking. redacted — volatiles are cheap on most platforms.

instead, use the initialization on demand holder idiom, which is thread-safe and a lot easier to understand:

this code is guaranteed to be correct because of the initialization guarantees for static fields; if a field is set in a static initializer, it is guaranteed to be made visible, correctly, to any thread that accesses that class.