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

深入理解Java內存模型(四):volatile特性、happens-before規則、內存語義實現與JSR-133增強歷程

     編輯:Java 相關知識

volatile 核心特性

將 volatile 讀/寫視為同一監視器鎖同步的單操作:

  • 可見性:讀總能見到任意線程最後的寫入
  • 原子性:單次讀/寫具原子性(含 64 位 long/double),復合操作(如 volatile++)非原子

volatile 寫-讀 happens-before 關係

class VolatileExample {
    int a = 0; volatile boolean flag = false;
    void writer() { a = 1; flag = true; } // 1→2
    void reader() { if (flag) { int i = a; } } // 3→4
}

happens-before 鏈路:

  1. 程序順序:1→2、3→4
  2. volatile 規則:2(寫 flag)→3(讀 flag)
  3. 傳遞性:1→4

volatile hb圖示

結果:線程 A 寫 flag 前對 a 的修改,線程 B 讀 flag 後立即可見。

volatile 內存語義

操作語義效果示意
volatile 寫刷新本地內存共享變量至主內存volatile寫
volatile 讀置本地內存無效,從主內存重讀volatile讀

本質:線程 A 通過主內存向線程 B 發送消息。

編譯器重排序規則表

編譯器重排序規則表

  • 第二操作為 volatile 寫 → 不重排(保證寫前操作不後移)
  • 第一操作為 volatile 讀 → 不重排(保證讀後操作不前移)
  • volatile 寫→volatile 讀 → 不重排

保守策略屏障插入方案

位置屏障類型目的
volatile 寫前StoreStore確保普通寫在 volatile 寫前刷新
volatile 寫後StoreLoad防止 volatile 寫與後續 volatile 讀/寫重排
volatile 讀後LoadLoad + LoadStore禁止 volatile 讀與後續普通讀/寫重排

volatile寫屏障

volatile讀屏障

優化實例:屏障消除

void readAndWrite() {
    int i = v1;  // volatile讀
    int j = v2;  // volatile讀
    a = i + j;   // 普通寫
    v1 = i + 1;  // volatile寫
    v2 = j * 2;  // volatile寫
}

優化後:優化後指令序列

  • 連續 volatile 讀之間無需 LoadLoad
  • volatile 寫前無需 StoreStore(前面無普通寫)
  • 最終 StoreLoad 不可省(方法返回,編譯器無法確認後續無 volatile 操作)

x86 處理器專項優化

x86 僅允許 Store-Load 重排 → 僅保留 volatile 寫後的 StoreLoad 屏障:x86優化

結論:x86 上 volatile 寫開銷遠大於讀(StoreLoad 成本高)。

JSR-133 增強 volatile 內存語義動機

舊模型允許 volatile 與普通變量重排:

a = 1;      // 1 普通寫
flag = true; // 2 volatile寫
// 可能重排為 2→1,導致讀線程見到 flag=true 但 a=0

舊模型重排

增強目標:嚴格限制 volatile 與普通變量重排,使 volatile 寫-讀具備鎖釋放-獲取同等語義,提供輕量級線程通信機制。

volatile vs 鎖 選擇指南

  • 功能:鎖更強(臨界區原子性),volatile 僅單操作原子
  • 性能/擴展性:volatile 優勢(無上下文切換、無鎖競爭)
  • 替換原則:極謹慎,僅適用於狀態標記、單次賦值等簡單場景
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved