Java拾遺2 - volatile和內存屏障和cpu指令

某一天看書, 發現AtomicInteger裏面有個lazySet方法. 很奇怪 不知道是幹啥. 所以就有了這篇文章.
更多拾遺系列文章

lazySet方法

源代碼:

    /**
     * Eventually sets to the given value.
     *
     * @param newValue the new value
     * @since 1.6
     */
    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }
    // 一般的set方法
        /**
     * Sets to the given value.
     *
     * @param newValue the new value
     */
    public final void set(int newValue) {
        value = newValue;
    }

其中value是volatile類型. 於是看了這篇文章: JUC中Atomic class之lazySet的一點疑惑 裏面解釋了lazySet和set的區別 就是減少了一個StoreLoad屏障.

爲啥會有內存屏障

我們知道cpu和內存之間實際上會有各級緩存在這裏插入圖片描述
在多線程環境下, 每個CPU都有自己緩存的數據, 那麼對於同一數據 可能在多個線程 (有可能在多個核心上執行)就很可能出現數據不一樣的情況? 那麼我們如何保證這樣的程序是正確的或者可預期的呢? 而不是毫無頭緒. 所以我們需要一種機制來讓別的處理器知道數據可能會過期. 所以就有了下面的幾種內存屏障原文:

  • LoadLoad屏障:對於這樣的語句Load1; LoadLoad; Load2,在Load2及後續讀取操作要讀取的數據被訪問前,保證Load1要讀取的數據被讀取完畢。
  • StoreStore屏障:對於這樣的語句Store1; StoreStore; Store2,在Store2及後續寫入操作執行前,保證Store1的寫入操作對其它處理器可見。
  • LoadStore屏障:對於這樣的語句Load1; LoadStore; Store2,在Store2及後續寫入操作被刷出前,保證Load1要讀取的數據被讀取完畢。
  • StoreLoad屏障:對於這樣的語句Store1; StoreLoad; Load2,在Load2及後續所有讀取操作執行前,保證Store1的寫入對所有處理器可見。它的開銷是四種屏障中最大的。在大多數處理器的實現中,這個屏障是個萬能屏障,兼具其它三種內存屏障的功能。
    ————————————————

有這麼多種是不是很難記? 實際上就3種, LoadLoad, LoadStore, StoreLoad. 因爲StoreStore並不能保證Load操作不被重排序, 所以在cpu中都沒有實現. 而在intel cpu中, LoadLoad對應了lfence指令. 這裏描述了內存屏障,

在這裏插入圖片描述
保證LoadLoad之前的所有Load操作都已經完成.

LoadStore對應了sfence指令.
在這裏插入圖片描述
保證的是 屏障前面的每個Store對屏障後的每個Store都可見.

StoreLoad對應了mfence.
在這裏插入圖片描述

爲啥StoreLoad是最慢的?

這裏這裏 解釋了爲啥StoreLoad屏障是最慢的.
從上面的StoreLoad/mfence可以看到, StoreLoad保證 屏障前面的所有Load. Store對屏障後的所有Load,或者Store都可見. – 很明顯這個就很強了.

爲啥會有重排序?

這裏 很好的解釋了爲啥要重排序, 以及爲啥重排序會更快.

References

  1. volatile使用的lock指令 - http://ifeve.com/juc-atomic-class-lazyset-que/
  2. 所有的指令下載地址百度盤
    鏈接: https://pan.baidu.com/s/1Wah0C3718BFo5idc0dQaWw 提取碼: nmux 複製這段內容後打開百度網盤手機App,操作更方便哦
    在這裏插入圖片描述
發佈了78 篇原創文章 · 獲贊 30 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章