Java 面試官最喜歡問的關鍵字 volatile

筆者去年面試過幾家公司,基本上每家公司都會問到volatile,甚至有的公司每輪面試的時候都會問到。面試官這麼喜歡問volatile就是因爲這個關鍵字涉及到的知識點較多比如Java內存模型、內存屏障、happen-befor等知識,可以繼續挖掘到系統指令、超線程等知識。

Java內存模型(JMM)

volatile是Java虛擬機提供的最輕量的同步機制,但很難被正確的理解與使用,通過學習Java內存模型對volatile專門定義的一些特殊訪問規則,或許會對理解volatile有一定幫助。

Java內存模型定義了線程和內存之間關係:線程之間的共享變量存儲在主內存中,每個線程都有一個私有的本地內存,本地內存中存儲了該線程以讀 / 寫共享變量的副本。本地內存是 JMM 的一個抽象概念,並不真實存在;它涵蓋內存、緩存、寄存器以及其他的硬件和編譯器優化。Java的內存模型抽象如下:

volatile的語義

volatile主要提供了兩種語義:

1,可見性:

可見性是指一個線程寫入的值,其他線程能夠立即讀取。在由Java內存模型可知道,每個線程都是有本地內存。所以線程A寫入在正常情況下,線程B不能立即讀取。但是在volatile變量,可以保證線程A不寫入本地內存直接寫入主內存,線程B直接從主內存中讀取,不從本地內存中讀取。

2,禁止指令重排序:

重排序是指編譯器和處理器爲了優化程序性能而對指令進行重排序的一種優化手段。

Java程序的幾種重排序

編譯器優化重排序:編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執行順序。
指令級並行的重排序:如果不存在數據依賴性,處理器可以改變語句對應機器指令的執行順序。
內存系統的重排序:處理器使用緩存和讀寫緩衝區,這使得加載和存儲操作看上去可能是在亂序執行
volatile的技術基石--內存屏障

內存屏障是cpu指令,該指令保證特定操作的順序性和某些內存的可見性。插入一條內存屏障指令之後會告訴編譯器和CPU:不管什麼指令都不能和這條指令重排序。 內存屏障所做的另外一件事情就是強制刷出各種CPU cache, 如一個Write-Barrier(寫入屏障)將刷出所有在Barrier之前寫入cache的數據,因此,任何CPU上的線程都能讀取到這些數據的最新版本。

對於Java程序而言, 如果把加入volatile關鍵字的代碼和未加入volatile關鍵字的代碼都生成彙編代碼,會發現加入volatile關鍵字的代碼會多出一個lock前綴指令。

volatile的典型用例

狀態標誌,代碼示例如下:

線程1執行run()的過程中,可能有另外的線程2調用了shutdown,所以stop變量必須是volatile(利用的volatile的可見性)。

還有一種常見的用法在雙重檢驗的單例實現上,代碼如下:

instance = new Singleton()這句,這並非是一個原子操作,事實上在 JVM 中這句話大概做了下面 3 件事情:

給 instance 分配內存
調用 Singleton 的構造函數來初始化成員變量
將instance對象指向分配的內存空間(執行完這步 instance 就爲非 null 了)
如果 instance變量沒有加volatile, 因爲指令重排序的存在,就可能導致執行步驟是1-2-3,也可能是1-3-2。一旦是1-3-2,就可能會導致訪問未初始化的內存。但是加上 volatile關鍵字之後,一定保證是按照1-2-3步驟執行的(利用的 volatile的禁止重排序 )。

歡迎工作一到五年的Java工程師朋友們加入Java架構師:697558955

羣內提供免費的Java架構學習資料(裏面有高可用、高併發、高性能及分佈式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章