final關鍵字內存語義

文章目錄


對於final修飾的變量,其實有兩個與指重排相關的規則

  • 如果final變量在構造函數中賦值,那麼就禁止這個賦值操作與構造函數的返回操作進行指令重排序
  • 如果一個對象中包含final變量,並且先訪問這個對象,然後訪問這個final變量,這兩個操作不允許發生指令重排序

我們所書寫的代碼順序和JVM執行的指令順序不一定是相同的,JVM會在不影響執行正確性的前提下對指令進行重排序,以達到提高執行效率的目的。當然,這個正確性指的是單線程下的正確性,如果是多線程的話,JVM是無法判斷的。

寫final

如果final變量在構造函數中進行賦值,那麼在編譯的時候,該賦值操作後面就會生成一個StoreStore內存屏障,用來保證構造函數返回之前,final變量必須賦值完成,以此來保證數據正確性。

而對於普通變量是沒有這個機制的,所以,如果普通內部變量在對象構造函數中賦值,普通變量的賦值操作可能會發生逃逸。一個線程構造了這個對象,另一個線程訪問這個對象,如果訪問成功說明這個對象的構造函數已經執行完了,並且按理來說普通變量應該也已經賦值完成了,但是因爲指令重排,普通變量的賦值操作可能逃逸出構造函數外了,導致普通變量的賦值有可能還未完成,此時訪問這個普通變量就會出現問題,而final變量不會有這個問題:

img

讀final

如果對象中包含final域,初次訪問這個對象,和初次訪問這個對象的final域,這兩個操作不允許重排序。這是因爲,如果訪問對象,說明對象的構造方法已經執行完畢了,也說明final變量也進行賦值了。那麼不會有問題;如果發生指令重排,先去訪問final變量,此時就會讀到還未賦值的final變量,就會出現問題。

具體的實現呢就是在編譯的時候,在final變量讀操作的前面加上一個LoadLoad內存屏障,來禁止指令重排

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章