可見性丶原子性和有序性

先說一下概念:

  • 可見性:
    一個線程對共享變量的修改,另外一個線程能夠立刻感知到,我們稱爲可見性;

  • 原子性
    一個或者多個操作在 CPU 執行的過程中不被中斷的特性稱爲原子性;

  • 有序性
    指令重排導致順序被打亂;


  線程工作內存: 是指 Cpu 的 ‘寄存器’‘高速緩存’,線程的 working memory 是cpu的寄存器和高速緩存的抽象描述,數據讀取順序優先級 是:寄存器->高速緩存->內存


可見性

線程工作空間導致可見性問題
在這裏插入圖片描述
  例如:線程A在主存中年將變量one=0拉去到自己的工作內存中,然後做了one = 5,當然這個操作是在cpu的寄存器中進行的,然後寫會高速緩存中,這時線程A的高速緩存還未執行同步主內存的操作,線程B又將one=0從主存拉取到了線程B的工作內存中,導致A線程已經更新但是B線程看不到的可見性問題;

原子性:

線程切換導致原子性問題 ++count
在這裏插入圖片描述

  例如:當線程A從主內存中將共享變量Count加載到線程A的工作內存後,發生了線程切換,這個時候線程B也將共享變量Count從主內存加載到了線程B的工作內存,這時線程A和B的工作內存中count都是0,線程B執行了Count = Count + 1,然後寫回到主內存,這時候線程切換完成,回到了線程A再次執行 Count = Count + 1,再將線程A工作內存計算過的count寫回主內存,現在我們得到的主內存呢中Count值是1而不是2。

有序性:

指令重排導致有序性問題;

在這裏講一個例子,就是獲取單例雙重檢查鎖(double-checked locking)判斷:

/**
 * @Auther: lantao
 * @Date: 2019-03-28 14:32
 * @Company: 隨行付支付有限公司
 * @maill: [email protected]
 * @Description: TODO
 */
public class Test1 {
    
    private DoMain doMain;
    
    public DoMain getDoMain(){
        if(doMain == null){
            synchronized (this.getClass()){
                if(doMain == null){
                    doMain = new DoMain("");
                }
                return doMain;
            }
        }else{
            return doMain;
        }
    }
}

  在上邊的代碼中在synchronized內和外都有一個if判斷,判斷doMain是否爲null操作,有很多人對synchronized中的if null判斷不理解,其實可以這樣想,線程A和線程B都執行到了synchronized這裏進行競爭鎖,結果A得到鎖,判斷if null,結果還未實例化,繼續進行實例化,然後return對象並釋放鎖,這時線程B獲取到了鎖進入if null判斷,發現doMain已經被線程A實例化過了,直接返回實例即可,第二個if null的作用就在這裏;

看上去上邊的代碼是完美的,但是new的操作上我們理解是:

  • 創建內存M
  • 在內存M上初始化doMain對象
  • 將內存M的地址指向變量doMain

但是實際上優化後(指令重排)的執行路徑可能是這樣的:

  • 創建內存M
  • 將內存M的地址指向變量doMain
  • 將內存M的地址指向變量doMain
    在這裏插入圖片描述

上圖大家應該都看的明白,我就不寫了,本文主要就是讓作者加深印象,本文是參考:https://time.geekbang.org/column/article/83682 總結的

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