筆者入職百度時,二面面試官的讓我聊聊C++之中的volatile關鍵詞。volatile在Java和C++之中的差別可謂是天差地別,我只是簡單聊了聊Java之中的volatile,面試官對我的回答并不滿意。后續(xù)學習《C++ Prmier》時,對volatile的理解也是云里霧里。入職百度之后,發(fā)現(xiàn)身邊的同學時候對volatile也是誤會頗多。(果然是“面試造核彈,工作擰螺絲”)所以筆者花了一些時間,整理了這篇文章,希望各位C++程序員能徹底厘清volatile。
1.volatile的誤會
volatile這個單詞在英文之中的意思是:易變的,不穩(wěn)定的的含義。所以顧名思義,一旦變量通過了volatile關鍵詞修飾之后,說明變量是易變的和不穩(wěn)定的。而C++之中最大的誤會就是認為volatile關鍵詞與并發(fā)編程有關,至于為何會引起這樣的誤會呢?筆者覺得罪魁禍首可能是下面的原因:
Java中的volatile
volatile影響最為深遠的就是Java之中的功用,筆者第一次接觸這個關鍵詞也是在Java之中。(加上數(shù)目龐大的Java程序員~~)Java之中volatile的效果是:
-
確保內(nèi)存可見性
讀和寫一個volatile變量具有全局有序性。每個線程訪問一個volatile變量時都會讀取它在內(nèi)存之中的當前值,而不是使用一個緩存中的值。這樣能夠確保當某線程對volatile變量進行了修改后,后面執(zhí)行的其他線程能看到volatile變量的變動。所以在簡單的多線程程序之中,volatile變量可以起到輕量級的同步作用。但是volatile關鍵字并不保證線程讀寫變量的相對順序,所以適用場景有限。 -
禁止指令重排序
指令重排序是JVM 為了提高程序的運行效率,在不影響單線程程序執(zhí)行結果的前提下,對各種指令執(zhí)行的過程進行重新排序和優(yōu)化,來增加程序處理時的并行度。指令重排序在單線程下能夠保證正確,但是在多線程的環(huán)境下就很難確保指令重排序的語義正確。
JDK5引入concurrent包中atomic,JDK6將synchronized關鍵字的性能優(yōu)化后。絕大多數(shù)場景,筆者都不再推薦使用volatile這個關鍵字了。
MSVC 微軟的鍋
早期的 MSVC之中 volatile 具有Release和Acquire語義,這我想給許多 C++程序猿造成了誤解。后續(xù)微軟將這個關鍵字做了一個切換:volatile:ms,用加 ms 的修飾來延續(xù)之前的語義。

2.volatile 的作用
其實上一節(jié)對volatile 的誤用做了討論。接下來筆者來帶大家看看,實際 volatile 關鍵字到底起到怎么樣的作用。先看如下的代碼:
int n = 10;
int main() {
int a = n;
int b = n;
return 0;
}
我們將這段代碼轉換為匯編代碼,筆者這里使用的** gcc 版本為5.4.0**:

接下來,我們將變量添加上 volatile關鍵字,看看會出現(xiàn)什么效果:
int n = 10;
int main() {
volatile int a = n;
volatile int b = n;
return 0;
}
重新編譯這部分代碼轉換為匯編代碼,我們來看看:

看到這部分匯編代碼,想必各位應該對 volatile關鍵詞的作用應該心中有數(shù)了。volatile相當于顯式的要求編譯器禁止對 volatile 變量進行優(yōu)化,并且要求每個變量賦值時,需要顯式從寄存器%eax拷貝。volatile 關鍵字在嵌入式編程之中會需要用到,在特定環(huán)境下,寄存器的變量可能會發(fā)生變化。volatile 所以聲明了寄存器部分的數(shù)據(jù)是『易變的』,需要防止編譯器優(yōu)化變量,強制載入寄存器。
但是如果需要實現(xiàn)類似 Java 之中 volatile 的效果呢?可以在代碼之中顯式插入內(nèi)存屏障,讓 CPU 強制刷新寄存器的變量到內(nèi)存之中。
int n = 10;
int main() {
volatile int a = n;
asm volatile("" ::: "memory");
volatile int b = n;
return 0;
}
現(xiàn)在我們再來看看編譯生成的匯編代碼:

由上述的匯編代碼我們可以看到,在添加了內(nèi)存屏障之后,對變量的賦值操作需要顯式的內(nèi)存之中取值。實際 Java 在實現(xiàn) volatile 關鍵字時,也是通過上述語句來實現(xiàn)的。
3.小結
volatile 關鍵字本身在 現(xiàn)代的C++和 Java 之中都不再推薦使用了。在 C++之中有很多對 volatile 的誤用。希望這篇文章能夠幫助大家解惑 volatile ,能夠正確的進行使用。學有不精,如有謬誤,請多多指教~~~