2017/12/10

使用 volatile 的變數


簡單的解釋: 在多執行緒的環境下,volatile 用於讓變數的讀和寫都在記憶體中執行。

因為每個執行緒都需將變數放在 CPU cache 才能做運算,所以要從記憶體中把所有可存取的變數複製一份副本到 CPU cache 以供運算時讀寫操作使用,此時就有可能因為某些運算造成 CPU cache 存的變數值和記憶體存的變數值不一致。volatile 可以保證變數的讀寫操作都在記憶體中執行,以確保在多個執行緒都可以取到最新的變數值。

換個說法,每個執行緒都有自己的堆疊,所以它可以存取自己的變數副本。當執行緒被建立時,它將所有可存取的變數複製到自己的記憶體中。volatile 關鍵字用於告訴 JVM 說:「注意,這個變數可以在其他執行緒中修改」。若沒有這個關鍵字,JVM 可以自由地進行一些最佳化,像是在某些執行緒中從不更新這些本地副本。volatile 會強制執行緒更新每個變數的原始值。volatile 關鍵字可以用在任何類型的變數,可以是 primitive 或 objects!

未使用 volatile 的變數


對於非 volatile 變數而言,JVM 並不能保證什麼時候把記憶體的變數值讀取到 CPU cache 中,以及何時把 CPU cache 的變數值寫入到記憶體中。

因此在多執行緒的環境下,有可能造成 Thread 1 將 counter 變數設為 7,但沒有馬上寫入到記憶體中,造成 Thread 2 取到 counter 之值仍為 0。
java-volatile-2.png
圖片來源

效能


由於 volatile 變數的讀寫都是在記憶體中執行,所以比直接在 CPU cache 做變數讀寫來得慢。
不過比 synchronized 快,因為 synchronized 要等到寫資料的執行緒做完才能夠讀到資料。

為何不使用 synchronized 關鍵字


除了效能比較慢之外,用 synchronized 有一個風險是,若正在讀取變數的執行緒因為某些原因臨時被暫停 (suspended) 的話,其他正在等待寫入同一個變數的執行緒會 陷入無限的等待直到讀取變數的執行緒終於做完

參考資料


Java Volatile Keyword Explained by Example
Java Volatile Keyword
[译] Java Volatile 关键字详解