final 域的內存語義理解

起點:

? ? ? ? 在舊的java內存模型中,線程可能會看到final域的值會改變,比如,一個線程當前看到一個整型final域的值為0(還未初始化之前的默認值),過一段時間之后這個線程再去讀取這個final域的值的時候,會發現值變為了1(被其他線程初始化之后的值)。常見的就是String類型的值可能會改變。

JSR-133 專家組增強了final域的語義,即通過為final域添加寫和讀的重排序規則,從而達到為java程序員提供初始化安全保證:只要對象是正確構造的(沒有發生被構造對象的引用在構造函數中溢出),那么不需要使用同步,就可以保證任意線程都能看到這個final域在構造函數中被初始化后的值。

1:寫final域規定

寫final域的重排序規則:禁止把final域的寫重排序到構造函數之外,實現機制是編譯器會在final域的寫之后,構造函數 return 之前,插入一個StoreStore屏障,這個屏障的作用就是禁止處理器把final域的寫重排序到構造函數之外。

寫final域的重排序規則可以確保:在對象引用為任意線程可用之前,對象的final域已經被正確初始化了,而普通域不具備這個保障,即普通域在實際構造函數中初始化時可能會被處理器重排序到構造函數之外,



這個

上圖所示發生了普通域的寫操作被編譯器重排序到了構造函數之外,導致線程B在讀取時還未完成初始化操作。

2:讀final域的規定

在一個線程中,初次讀對象引用與初次讀該對象包含的final域,JMM禁止處理器重排序這兩個操作,這個規則僅僅針對處理器。編譯器的實現是在讀final域的前面插入一個 loadload 屏障。

讀final域的重排序規則可以保證:在讀一個對象的final域之前,一定會先讀包含這個final域的對象的引用。即如果該引用不為null,那么引用對象的final域一定已經被初始化了。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容