深度剖析Java的volatile實現原理,再也不怕面試官問了( 二 )


總線嗅探是通過CPU偵聽總線上發生的數據交換操作,當總線上發生了數據操作,那么總線就會廣播對應的通知,CPU收到通知后,再根據本地的情況進行響應 。
5. 有序性問題虛擬機在進行代碼編譯時 , 對改變順序后不會對最終結果造成影響的代碼,虛擬機不一定會按我們寫的代碼順序運行,有可能進行重排序 。實際上雖然重排后不會對變量值有影響 , 但會造成線程安全問題 。
重排序又可以分為三種:

  • 編譯器優化的重排序 。編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執行順序
  • 指令級并行的重排序 。現代CPU采用了指令級并行技術來將多條指令重疊執行 。對于不存在數據依賴的指令,CPU可以改變語句對應機器指令的執行順序
  • 內存系統的重排序 。由于CPU使用三級緩存結構,這使得數據加載和存儲操作看上去可能是在亂序執行的
不過重排序也不是隨便重排的 , 發生指令重排序的前提是:在單線程下不影響執行結果、對沒有數值依賴的代碼進行重排序 。這就是as-if-serial語義 。在多線程情況下有一套更具體的規則,那就是happens-before原則 。
happens-before由以下八大原則組成:
  • 程序次序規則:一個線程內 , 按照代碼順序,書寫在前面的操作先行發生于書寫在后面的操作(線程的執行結果有序)
  • 鎖定規則:一個unlock操作先行發生于后面對同一個鎖的lock操作
  • volatile變量規則:對一個volatile變量的寫操作先行發生于后面對這個變量的讀操作
  • 傳遞規則:如果操作A先行發生于操作B,操作B先行發生于操作C,則可以得出操作A先行發生于操作C
  • 線程啟動規則:Thread對象的start()方法先行發生于該線程的其他任何操作
  • 線程中斷規則:對線程中斷方法interrupt()的調用先行發生于被中斷線程檢測到中斷事件的發生
  • 線程終結規則:線程中所有操作先行發生于線程的終止檢測 。通過Thread.join()方法結束、Thread.isAlive()方法的返回值等手段檢測到線程已經終止執行 。比如在A線程中調用B.join()方法 , B線程執行完成后,B對共享變量的修改,對A來說是可見的
  • 對象終結規則:一個對象的初始化方法完成先行發生于該對象的finalize()方法的開始
如果兩個操作不滿足上述八大原則中的任意一個,那么這兩個操作就沒有順序保證,虛擬機可以對這兩個操作進行重排序 。如果操作A happens-before 操作B , 那么A在內存所做的修改對B都是可見的 。
而volatile是通過插入內存屏障(Memory Barrier),在內存屏障前后禁止重排序優化,以此實現有序性 。
內存屏障有兩個作用:一是保證特定操作的執行順序,二是保證某些變量的內存可見性 。
volatile內存語義的實現: JMM 針對編譯器制定的 volatile 重排序規則表
操作普通讀寫volatile讀volatile寫普通讀寫可以重排可以重排不可以重排volatile讀不可以重排不可以重排不可以重排volatile寫可以重排不可以重排不可以重排編譯器在生成字節碼時 , 會在指令序列中插入內存屏障來禁止特定類型的處理器重排序:
  • 在每個volatile寫操作的前面插入一個StoreStore屏障
  • 在每個volatile寫操作的后面插入一個StoreLoad屏障
  • 在每個volatile讀操作的后面插入一個LoadLoad屏障
  • 在每個volatile讀操作的后面插入一個LoadStore屏障
6. volatile應用場景volatile可以保證可見性和有序性,但無法保證原子性 。所以它的應用場景就不如synchronized廣泛,主要有兩個場景:一是做狀態變量 , 二是做需要重新賦值的共享對象 。
比如:第二種場景常見的就有修飾單例模式的對象 。
public class Singleton {// 使用volatile修飾,賦值后,其他線程能立即感知到private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}}還有就是CopyOnWriteArrayList的底層實現就是用volatile修飾的數組,因為CopyOnWriteArrayList每次修改數據后都會數組重新賦值,而不是只修改數據中的一個值,這樣才能保證了CopyOnWriteArrayList的數據安全性 。
深度剖析Java的volatile實現原理,再也不怕面試官問了

文章插圖
我是「一燈架構」,如果本文對你有幫助,歡迎各位小伙伴點贊、評論和關注,感謝各位老鐵,我們下期見

推薦閱讀