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

深入理解Java內存模型(二):重排序類型、as-if-serial語義與多線程影響實例剖析

     編輯:Java 相關知識

數據依賴性與 as-if-serial 語義

數據依賴類型(重排會改變結果):數據依賴類型

  • 寫後讀(流依賴)
  • 寫後寫(輸出依賴)
  • 讀後寫(反依賴)

編譯器/處理器遵守單線程/單處理器內數據依賴,不重排存在依賴的操作。跨線程/處理器依賴不被考慮。

as-if-serial 語義

無論如何重排序,單線程程序執行結果不能改變。編譯器、runtime、處理器共同遵守,為單線程程序員創造「按程序順序執行」的幻覺。

示例:計算圓面積

double pi = 3.14; // A
double r = 1.0;   // B
double area = pi * r * r; // C

依賴關係:A→C、B→C。A、B 無依賴可重排。依賴關係

兩種合法執行順序:執行順序

程序順序規則與 happens-before 微妙定義

示例存在 3 個 hb:A→B、B→C、A→C(傳遞性)。

關鍵認知:A hb B 不要求 A 必須在 B 前執行,僅要求:

  1. A 執行結果對 B 可見
  2. A 按順序排在 B 之前

重排 A、B 不改變結果且 A 結果無需對 B 可見 → JMM 允許。體現「不改變結果前提下最大化並行度」共同目標。

重排序對多線程的破壞性影響

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

情況 1:操作 1、2 重排(寫旗標先於寫數據)

1、2重排

線程 A 先寫 flag=true,線程 B 讀到 flag 進入判斷,但 a 尚未寫入 → 讀到錯誤值 0。**多線程語義被破壞!**

情況 2:操作 3、4 重排(控制依賴與猜測執行)

3、4重排

  • 3、4 存在控制依賴
  • 處理器採用猜測執行:提前計算 a*a 存入重排序緩衝 (ROB),條件為真時寫入 i
  • 本質上重排了 3、4 → 同樣導致讀到舊值

關鍵差異:單線程中控制依賴重排不改變結果(as-if-serial 允許);多線程中可能改變程序語義!

總結

  • 重排序受數據依賴與 as-if-serial 约束
  • 單線程語義受保護,多線程語義易被破壞
  • 需顯式同步(volatile、鎖)建立跨線程 happens-before 關係
  • 理解重排序是掌握 JMM 乃至並發編程的基石
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved