首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

深入理解Java內存模型(六):final域語義、重排序規則與初始化安全性保證

     編輯:Java 相關知識

final 域兩大重排序規則

  1. 寫規則:構造函數內對 final 域的寫入,不能重排序到構造函數外(賦值引用給變量之前)
  2. 讀規則:初次讀對象引用與初次讀該對象 final 域,不能重排序(間接依賴)

寫 final 域規則實現機制

  • 編譯器層面:禁止 final 域寫移至構造函數外
  • 處理器層面:構造函數 return 前插入 StoreStore 屏障,防止 final 域寫重排到引用賦值之後

寫final域重排序示意

示例中,普通域 i 寫被重排到構造函數外導致讀線程見到舊值,final 域 j 受規則保護必在構造函數內完成初始化,讀線程必見新值。

讀 final 域規則實現機制

  • 編譯器遵守間接依賴,不重排序
  • 處理器層面:讀 final 域前插入 LoadLoad 屏障,針對允許間接依賴重排的處理器(如 Alpha)

讀final域重排序示意

讀普通域被重排到讀引用前見到舊值,final 域受規則保護必在讀引用後讀取,見到初始化值。

final 引用類型的特殊規則

對於 final int[] arr:

  • 構造函數內對 final 引用對象的成員域寫入(如 arr[0]=1),不能重排到構造函數外引用賦值之後
  • 保證:讀線程至少能見到構造函數內對 final 引用對象成員域的寫入
  • 後續其他線程對該對象成員域的寫入(如線程 B 寫 arr[0]=2),不保證可見,存在數據競爭需額外同步

final引用類型示意

final 引用不得在構造函數中「逸出」

public FinalReferenceEscapeExample() {
    i = 1;          // 1 寫final域
    obj = this;     // 2 this引用逸出!此時對象未構造完成
}

引用逸出示意

操作 1、2 可能重排,導致其他線程通過逸出引用見到未初始化的 final 域。構造函數返回前,對象引用不可公開。

x86 處理器上的 final 語義實現

  • x86 不重排寫-寫 → StoreStore 屏障省略
  • x86 遵守間接依賴 → LoadLoad 屏障省略
  • 結論:x86 上 final 讀/寫零屏障開銷!

JSR-133 為何增強 final 語義

舊模型允許 final 域值「變化」:線程先見默認值 0,後見初始化值 1。最典型受害者是 String(char[] value + offset/count),舊模型下 String 可能出現值變化。新規則賦予 初始化安全性:正確構造的對象,無需同步即可保證所有線程見到 final 域初始化後的值。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved