java內存模型

併發編程模型的分類

  • 併發編程中遇到的兩個問題: 線程之間如何通訊, 線程之間如何同步
  • 以線程之間的通訊模式來看, 併發模型能夠分爲兩種
    ** 共享內存的併發模型
    ** 消息傳遞的併發模型
  • java併發採用的是共享內存模型

JMM抽象模型

  • java中, 堆內存是線程之間共享的, 其中包括了實例域、靜態域、數組元素, 而局部變量、
    方法參數、異常處理參數是線程私有的, 不會有內存可見性問題, 不受內存模型影響
  • JMM決定了一個線程對共享變量的寫入什麼時候對另外一個線程可見
  • 從抽象角度來看, 共享變量存儲在主內存中, 每一個線程有一個副本, 讀寫在這個副本上, 最終由JMM決定與主內存的同步
    ** 線程A向主內存刷新變動, 線程B向主內存讀取更新, 實現線程通訊
  • java內存模型

重排序

  • 編譯器優化重排序
  • 指令級並行的重排序
  • 內存系統的重排序
  • JMM屬於語言級的內存模型, 它確保了在不一樣編譯器與不一樣處理器平臺上, 經過禁止特定的重排序, 爲程序員提供一致的內存可見性

內存屏障

實現cpu數據可見性, 禁止指令重排序java

java內存模型如何定義併發關鍵字的行爲

  • synchronized
    ** 線程釋放monitor, 把cpu緩存(線程本地緩存)刷新到主內存
    ** 線程得到monitor, 把cpu緩存失效, 強制從主內存獲取
    ** 禁止特定的指令重排, synchronized關鍵字包裹的代碼不能夠重排序到外面
  • volatile
    ** volatile修飾的變量, 每次的變動都會刷新到主內存, 每次讀取這種變量前, 也必須保證緩存無效
    ** 禁止特定的指令重排
  • final
    ** final修飾的變量, 在正確的構造對象後, 對其它線程是可見的

happens-before

雙重檢查鎖定(double-check-locking)

當咱們寫出一個標準的雙重檢查鎖定單例模式時(以下), 代碼還是有問題的, 這是由於重排序的緣故,
線程有可能拿到還沒有實例化的對象的引用.
使用volatile修飾類變量, 禁止helloSingleton = new HelloSingleton()這步的指令重排.程序員

private static HelloSingleton helloSingleton;
    public static HelloSingleton instance() {
        if (helloSingleton == null) {
            synchronized(SingletonFactory.class) {
                if (helloSingleton == null) {
                    return helloSingleton = new HelloSingleton();
                }
            }
        }
        return helloSingleton;
    }

還有一種更加優雅的方式, 使用內部類實現, 內部類會在被引用時才加載, 實現了懶加載編程

單例模式實現方式有好多種,但大部分都會有多線程環境下的問題;使用內部類能夠避免這個問題,由於在多線程環境下,jvm對一個類的初始化會作限制,同一時間只會容許一個線程去初始化一個類,這樣就從虛擬機層面避免了大部分單例實現的問題數組

public static class innerHolder {
        public static HelloSingleton helloSingleton = new HelloSingleton();
    }
    public static HelloSingleton instanceV5() {
        return innerHolder.helloSingleton;
    }

參考資料

深刻理解Java內存模型(一)——基礎緩存

什麼是Java內存模型多線程

雙重檢查鎖定併發

相關文章
相關標籤/搜索