一個作業了6年的Java程式員,在阿里二面,被問到“volatile”關鍵字,
然后,就沒有然后了…
同樣,另外一個去美團面試的作業4年的小伙伴,也被“volatile關鍵字“,
然后,也沒有然后了…
這個問題說實話,是有點偏底層,但也的確是并發編程里面比較重要的一個關鍵字,
下面,我們來看看普通人和高手對于這個問題的回答吧,
普通人:
嗯… volatile可以保證可見性,
高手:
volatile關鍵字有兩個作用,
- 可以保證在多執行緒環境下共享變數的可見性,
- 通過增加記憶體屏障防止多個指令之間的重排序,
我理解的可見性,是指當某一個執行緒對共享變數的修改,其他執行緒可以立刻看到修改之后的值,
其實這個可見性問題,我認為本質上是由幾個方面造成的,
-
CPU層面的高速快取,在CPU里面設計了三級快取去解決CPU運算效率和記憶體IO效率問題,但是帶來的就是快取的一致性問題,而在多執行緒并行執行的情況下,快取一致性就會導致可見性問題,

所以,對于增加了volatile關鍵字修飾的共享變數,JVM虛擬機會自動增加一個#Lock匯編指令,這個指令會根據CPU型號自動添加總線鎖或/快取鎖
我簡單說一下這兩種鎖,
- 總線鎖是鎖定了CPU的前端總線,從而導致在同一時刻只能有一個執行緒去和記憶體通信,這樣就避免了多執行緒并發造成的可見性,
- 快取鎖是對總線鎖的優化,因為總線鎖導致了CPU的使用效率大幅度下降,所以快取鎖只針對CPU三級快取中的目標資料加鎖,快取鎖是使用MESI快取一致性來實作的,
-
指令重排序,所謂重排序,就是指令的撰寫順序和執行順序不一致,在多執行緒環境下導致可見性問題,指令重排序本質上是一種性能優化的手段,它來自于幾個方面,
- CPU層面,針對MESI協議的更進一步優化去提升CPU的利用率,引入了StoreBuffer機制,而這一種優化機制會導致CPU的亂序執行,當然為了避免這樣的問題,CPU提供了記憶體屏障指令,上層應用可以在合適的地方插入記憶體屏障來避免CPU指令重排序問題,
- 編譯器的優化,編譯器在編譯的程序中,在不改變單執行緒語意和程式正確性的前提下,對指令進行合理的重排序優化來提升性能,
所以,如果對共享變數增加了volatile關鍵字,那么在編譯器層面,就不會去觸發編譯器優化,同時再JVM里面,會插入記憶體屏障指令來避免重排序問題,
當然,除了volatile以外,從JDK5開始,JMM就使用了一種Happens-Before模型去描述多執行緒之間的記憶體可見性問題,
如果兩個操作之間具備Happens-Before關系,那么意味著這兩個操作具備可見性關系,不需要再額外去考慮增加volatile關鍵字來提供可見性保障,
以上就是我對這個問題的理解,
總結
在我看來,并發編程是每個程式員必須要掌握好的領域,它里面涵蓋的設計思想、和并發問題的解決思路、以及作為一個并發工具,都是非常值得深度研究的,
我推薦大家去讀一下《Java并發編程深度決議與原理實戰》這本書,對Java并發這塊的內容描述得很清晰,
好的,本期的普通人VS高手面試系列就到這里結束了,喜歡的朋友記得點贊和收藏,
另外,有任何技術上的問題,職業發展有關的問題,都可以私信我,我會在第一時間回復,
著作權宣告:本博客所有文章除特別宣告外,均采用 CC BY-NC-SA 4.0 許可協議,轉載請注明來自
Mic帶你學架構!
如果本篇文章對您有幫助,還請幫忙點個關注和贊,您的堅持是我不斷創作的動力,歡迎關注「跟著Mic學架構」公眾號公眾號獲取更多技術干貨!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/467925.html
標籤:Java
上一篇:如何生成一個java檔案
下一篇:中斷執行緒的方法分享
