Java final變數與final類的記憶體模型

2021-07-27 17:05:00 字數 1713 閱讀 7053

對於 final 域,編譯器和處理器要遵守兩個重排序規則:

舉個例子:

public class finalexample 

public static void writer()

public static void reader()

}

這裡假設乙個執行緒 a 執行 writer ()方法,隨後另乙個執行緒 b 執行 reader ()方法。

在寫 final 域的時候有兩個規則:

分析上面的**。write 方法,只包含一行 obj = new finalexample();,但是包含兩個步驟:

假設執行緒 b 當中讀 obj 與讀成員域之間沒有重排序。那麼執行時序可能如下:

寫 final 域的重排序規則可以確保:在物件引用為任意執行緒可見之前,物件的 final 域已經被正確初始化過了,而普通域不具有這個保障。

讀 final 域的重排序規則如下:

reader() 方法包含三個操作:

現在我們假設寫執行緒 a 沒有發生任何重排序,那麼執行時序可能是:

上面的圖可以看到對普通變數 i 的讀取重排序到了讀物件引用之前,在讀普通域時候,該域還沒被寫執行緒 a 寫入,這是乙個錯誤的讀取操作。而讀 final 域已經被 a 執行緒初始化了,這個讀取操作是正確的。

讀 final 域的重排序規則可以確保:在讀乙個物件的 final 域之前,一定會先讀包含 這個 final 域的物件的引用。在這個示例程式中,如果該引用不為 null,那麼引用 物件的 final 域一定已經被 a 執行緒初始化過了。

如果 final 域是引用型別,寫 final 域的重排序規則對編譯器和處理器增加了如下約束:

如下**例子:

public class finalreferenceexample 

public static void writerone()

public static void reader() }}

假設首先執行緒 a 執行 writerone()方法,執行完後執行緒 b 執行reader 方法,jmm 可以確保讀執行緒 b 至少能看到寫執行緒 a 在建構函式中對 final 引用物件的成員域的寫入。

**如下:

public class finalreferenceescapeexample 

public static void writer()

public static void reader() }}

假設乙個執行緒 a 執行 writer()方法,另乙個執行緒 b 執行 reader()方法。

這裡的操作 2 使得物件還未完成構造前就為執行緒 b 可見。即使這裡的操作 2 是建構函式的最後 一步,且即使在程式中操作 2 排在操作 1 後面,執行 read()方法的執行緒仍然可能無 法看到 final 域被初始化後的值,因為這裡的操作 1 和操作 2 之間可能被重排序。

在建構函式返回前,被構造物件的引用不能為其他執行緒可 見,因為此時的 final 域可能還沒有被初始化。在建構函式返回後,任意執行緒都將 保證能看到 final 域正確初始化之後的值。

java final變數 方法 類

final資料 class value class finaldata public final int valueone 9 public static final int value two 99 public static final int value three 39 public fin...

lambda 內使用的區域性變數必須是final的

在開發 中遇到乙個問題,使用lambda遍歷map,外部有乙個string,在迴圈中把 string 字串的關鍵字進行替換,但是一直不能替換成功。錯誤提示,使用的這個變數必須是final或者是實際上final的?為什麼?lambda是乙個匿名內部類,內部類是指在乙個外部類內部再定義乙個類。內部類可以...

Java final關鍵字與多型全解析

修飾方法 被修飾的方法不能被重寫 修飾變數 1.被final 修飾的物件的位址不能改變 不能重寫指向 2.final 基本資料型別變數 不能被重寫賦值 修飾類 被修飾的類不能被繼承 舉例分析 public class demo01 class classa class classb extends ...