1. final的指令重排
對於 final 域,編譯器和處理器要遵守兩個重排序規則。
- 在構造函數內對一個
final變量
賦值,與隨後把這個被構造對象的引用賦值給一個變量,這兩個操作之間不能重排序 - 初次讀一個包含final變量的對象,與隨後初次讀這個final變量,這兩個操作之間不能重排序
public class FinalExample {
int i; // 普通變量
final int j; // final 變量
static FinalExample obj;
public FinalExample () { // 構造函數
i = 1; // 寫普通域
j = 2; // 寫 final 域
}
public static void writer () { // 寫線程 A 執行
obj = new FinalExample ();
}
public static void reader () { // 讀線程 B 執行
FinalExample object = obj; // 讀對象引用
int a = object.i; // 讀普通域
int b = object.j; // 讀 final 域
}
}
1.1. 賦值final屬性
的指令重排
賦值final屬性
的重排序規則禁止把final屬性
的賦值操作重排序到構造函數之外,這個規則的實現包含下面2個方面
- JMM 禁止編譯器把
final屬性
的賦值操作重排序到構造函數之外 - 編譯器會在 final 域的寫之後,構造函數 return 之前,插入一個 StoreStore 屏障。這個屏障禁止處理器把 final 域的寫重排序到構造函數之外
寫 final 域的重排序規則可以確保:在對象引用爲任意線程可見之前,對象的 final 域已經被正確初始化過了,而普通域不具有這個保障
1.2. 讀取final屬性
的指令重排
讀 final 域的重排序規則是,在一個線程中,初次讀對象引用與初次讀該對象包含的 final 域,JMM 禁止處理器重排序這兩個操作.編譯器會在讀 final 域操作的前面插入一個 LoadLoad 屏障
1.3. final屬性
爲引用類型
對於引用類型,賦值final屬性
的重排序規則對編譯器和處理器增加了如下約束:
在構造函數內對一個引用類型的final屬性
的賦值,與隨後在構造函數外把這個被構造對象的引用賦值給一個引用變量,這兩個操作之間不能重排序