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