Java多線程(併發)淺談(二)

    前面我們講到過了線程的一些性質,以及Synchronizd關鍵字的的用法以及原理(保整理原子性,可見性)。今天介紹的也是個關鍵字(volatile - 保證了可見性)

    可見性:可見性就是當一個線程執行的時候,每次的讀取都是最新的值,不存在值已經更新了,但是取出的值還是原來的值(否則那隻能說明更新失敗等)。但在多線程的情況下,這種情況就是很有可能發生,例如:線程A 更新了主內存的某個值,但是此時線程B剛好要獲取這個值,因此這個時候線程B獲取的值很有可能就是歷史的值了,因爲這個時候線程A 修改後的值還未被更新到主內存上,這就是所謂的可見性 

    那麼應該如何保證可見性的呢?基於GP-Mic老師的課程我這邊也從硬件層面和軟件層面來說說我對這個一塊的理解吧!

  硬件層面 (CPU ,I/O,內存等  CPU (高速緩存)  --> 正式因爲有高速緩存的存在所以會引發一個問題 :緩存一致性問題。爲什麼這麼說? 在讀個CPU的情況下,每次的更新都會觸發主內的更新,但是這個更新並非實時的。引出了總線鎖和緩存鎖

     總線鎖緩存鎖

      總線鎖:當有多個線程同時訪問主內存的數據時,這個時候總線會發出一個Lock的指令,只允許一個處理器進行修改,其他都只能等待。顯然這個是不行,這個會嚴重影響到效率,本來並行,結果總線鎖了,變成了串行了。

      緩存鎖 :可以看做是一種優化,多個CPU同時緩存了主內存的數據,當有修改的時候,這個時候就會用上緩存鎖了,因此並不會加在總線上. 先修改本內部的值,然後通過緩存一致性協議(MESI)來保證可見性

MESI 

M(Modify) : 表示共享數據只緩存在當前CPU緩存中,並且是被修改狀態,也就是緩存的數據和主內
存中的數據不一致 

E(Exclusive) :表示緩存的獨佔狀態,數據只緩存在當前CPU緩存中,並且沒有被修改

S(Shared): 表示數據可能被多個CPU緩存,並且各個緩存中的數據和主內存數據一致

I(Invalid) :表示緩存已經失效

                       

這個時候我們發現,在更新操作的時候,CPU0需要通知其他CPU這個數據已失效,而這個過程是處於阻塞中,因此這個也是不可以取的。

  這個時候又引入了storeBuffers,它的作用就是不讓CPU0處於等待中,由原來的同步變成了異步。那麼這個時候又會引入一個新的問題,什麼問題呢? 因爲storeBuffer的引入會導致指令重排序的問題,什麼是指令重排序呢?

爲了解決這個指令重排序的問題,通過內存屏障來解決這個問題。

前面的操作對後面的操作是可見的

軟件層面

  通過JMM來定義了一個規範,簡單來說,JMM定義了共享內存中多線程程序讀寫操作的行爲規範.JMM是一個抽象模型,它是建立在不同的操作系統和硬件層面之上對問題進行了統一的抽象,然後再Java層面提供了一些高級指令,讓用戶選擇在合適的時候去引入這些高級指令來解決可見性問題

通過提供的這些高級指令來保證可見性,以及重排序問題(比如今天要說的 Volatile)

Volatile 的原理 (Happens-Before模型前面的操作對於後面是可見的

Happens-Before模型  

1、程序的順序規則 (即在不影響到最終結果的情況下,保證程序的有序性)

2、傳遞性規則 (a happens-bdfore b ,b happens-before c   --> a happens-before c) 

3、volatile 變量規則

     volatile int a = 1;

4、synchronized規則

5、start規則

   Thread thread  = new Thread();

    flag = fasle;

    thread.start();

6、join 規則

 thread .start();
  thread .join();

 

 

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