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

深入理解 Java 內存模型:volatile 關鍵字詳解

     編輯:Java 綜合教學

volatile 的核心特性

將共享變量聲明為 volatile 後,對該變量的單次讀/寫操作具備以下特性:

  • 可見性:對一個 volatile 變量的讀,總能看到任意線程對該變量最後的寫入。
  • 原子性:對單個 volatile 變量的讀/寫具有原子性(含 64 位的 long/double),但復合操作(如 volatile++)不具備原子性。

這等價於使用同一把鎖同步該變量的單次讀/寫,但鎖能保證臨界區整體原子性,volatile 僅限單次操作。

volatile 寫-讀建立的 Happens-Before 關係

從 JSR-133 起,volatile 寫-讀可實現線程間通信,其內存語義與鎖釋放-獲取一致:

  • volatile 寫 ≒ 鎖釋放:將本地內存共享變量刷新到主內存。
  • volatile 讀 ≒ 鎖獲取:使本地內存失效,從主內存重新讀取。

示例:線程 A 執行 writer()(寫 a=1 再寫 flag=true),線程 B 執行 reader()(讀 flag 再讀 a)。因 flag 為 volatile,A 寫 flag 前的所有共享變量修改,在 B 讀 flag 後對 B 可見。

內存語義實現:編譯器重排序規則與內存屏障

編譯器重排序規則表

能否重排序第二個操作第一個操作
普通讀/寫volatile 讀volatile 寫
普通讀/寫NONO
volatile 讀NONO
volatile 寫NONO

規則確保:volatile 寫之前的操作不會被重排到寫之後;volatile 讀之後的操作不會被重排到讀之前。

處理器層面:內存屏障插入策略(保守策略)

  • volatile 寫前插入 StoreStore 屏障。
  • volatile 寫後插入 StoreLoad 屏障(全能屏障,開銷最大)。
  • volatile 讀後插入 LoadLoad 和 LoadStore 屏障。

在 x86 處理器上,除 StoreLoad 外其它屏障均可省略,故 volatile 寫開銷遠大於讀。

為何 JSR-133 增強 volatile 語義?

舊模型允許 volatile 與普通變量重排序,導致 volatile 寫-讀不具備鎖的內存語義。JSR-133 通過禁止此類重排序,使 volatile 成為比鎖更輕量的線程通信機制。

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