final关键字_深入分析Java中的final关键字
Java中被final修飾的變量與普通變量有何區別?被final修飾的變量不可更改、被final修飾的方法不可重寫是怎樣做到的?帶著疑問我們一點點撥開云霧。
一、final的內存定義及規則
對于final關鍵字,編譯器、處理器從讀寫兩個角度限制了其使用規則:
- 對于一個類的final修飾的變量,如果在定義是不指定初始值,那么在構造函數中必須進行初始化,在構造函數中進行final域的寫入時,隨后將構造后的對象引用賦值給另外一個引用變量,它們之間不能進行重排序的發生。
- 在讀一個包含final關鍵字的對象引用和讀這個引用的包含的final修飾的變量時,這兩個操作間不能發生重排序。
下面通過一段代碼分析一下具體場景:
public這里先假設A線程執行finalWriter方法,B線程執行finalRead()方法,通過上述對于final的規則描述我們分析一下finalWriter方法的執行流程:
對于final修飾的變量進行賦值操作時的重排序規則如下:
1、Java內存模型禁止將對final關鍵字修飾的變量進行寫操作重排序到構造函數之外。2、編譯器會在寫之后,構造函數return之前插入StoreStore屏障,這個屏障確保編譯器不會把final變量寫操作重排序到構造函數之外。
下面假定一種重排序的場景如下圖所示:
final域的寫流程上圖的場景情況為變量i為普通變量,在進行賦值時發生了重排序(由于這時候有可能構造函數還未完成),在構造函數結束后,才進行了賦值,線程B讀取到的i的值為賦值前的初始值0,而對于final修飾的變量j由于禁止重排序,在構造函數return前需要進行賦值,限定到了構造函數內,讀取到的變量j為正確的值。
然后再分析一下執行finalRead()方法的流程:
對于final修飾的讀操作重排序規則:
在一個線程中首次讀對象的引用和首次讀該對象包含的final修飾的變量,Java內存模型禁止重排序(也就是說在讀取一個final修飾的變量前,一定是先獲取該變量對應的引用),主要原理就是在讀取final修飾的變量前插入LoadLoad屏障。假設上述情況,線程A正常執行,變量i沒有發生重排序的情況,而對于線程B讀取變量i和讀對象的引用發生了重排序,如下圖所示
讀對象的普通變量i時處理器發生了重排序,讀變量在讀對象的引用之前發生,這時候變量還未開始進行賦值,而對于final修飾的變量j來說,由于其遵循重排序規則(讀變量首先要讀變量對應的對象引用),所以讀取的值是正確的。
除了上述兩種場景之外,假設在構造函數內使用this關鍵字將當前對象賦值給成員變量(逸出),如下代碼所示:
public這時候同樣有兩個線程,一個線程執行finalWriter,另外一個執行finaRead,也有可能會出現被final修飾的變量j沒有進行賦值的情況。
參考《Java并發編程的藝術》掃碼關注“聊點源碼”獲取更多資訊總結
以上是生活随笔為你收集整理的final关键字_深入分析Java中的final关键字的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html玫瑰花效果代码,html5渲染3
- 下一篇: C++头插法尾插法建立单链表,合并两个有