Java併發編程(2)-併發原理

摘要

咱們這一講主要講解基於volatile實現併發:可見性跟有序性問題,講解volatile的時候,須要講解:cpu緩存模型 -> java內存模型 -> 併發編程3大特性:原子性、可見性、有序性 -> volatile的做用 -> volatile的底層原理 -> volatile實戰。java

思惟導圖

內容

cpu多級緩存模型

1.volatile引入

咱們先看下如下例子:編程

public class VolatileTest {
     static int flag = 0;
    public static void main(String[] args) {
        /**
         * 一、開啓一個讀線程,讀取flag的值
         */
        new Thread(){
            @Override
            public void run() {
              int localFlag = flag;
              while (true){
                 if(localFlag != flag){
                      System.out.println("讀取到的標識位值:"+flag);
                      localFlag = flag;
                  }
                  try {
                      TimeUnit.SECONDS.sleep(2);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
            }
        }.start();
        /**
         * 二、開啓一個讀寫線程,修改flag值
         */
        new Thread(){
            @Override
            public void run() {
                int localFlag = flag;
                while (true){
                    System.out.println("標識位被修改了:"+ ++localFlag);
                    flag = localFlag;
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}

結果輸出爲:
image.png緩存

經過以上咱們發現:
一、上面代碼實例中:flag是靜態成員變量,存在與方法區;此方法區裏面的資源是線程共享的資源,線程共享資源在多線程數據讀取跟修改時候,會出現線程安全問題。而localFlag是方法棧裏面的變量是線程私有的數據。每次是把堆裏面的數據讀取出來賦值給棧裏面的數據。
二、共享數據在多線程讀寫時候,會出現線程不一致性問題;讀線程不可以準確讀取到寫線程修改的數值。
三、在實際的系統運行過程當中,可能會產生一個問題,就是說,Thread1修改變量的值,他修改了這個變量的值,結果呢,發現Thread2在他修改變量值以後,沒那麼快能感知到flag數值的變化。Thread1,已經將flag設置好了;可是Thread2,好比說在一段時間範圍內,仍是讀到了舊的flag,在一小段時間範圍內,可能Thread2會感知不到Thread1對flag的值修改,他讀到的可能仍是flag的這麼一箇舊的值。 安全

咱們將變量加上volatile修飾多線程

static volatile int flag = 0;

輸出結果以下:
image.png
咱們發現加上volatile以後,每次讀線程都可以準確獲取到volatile修飾的數據。併發

併發編程中:你只要開了多個線程,必定會有一些這種問題,某個線程修改一個變量值,其餘線程要馬上感知到這個變量值的變化,可是若是你不用volatile,會有問題:有線程修改了一個變量的值,結果其餘的線程感知不到這個變量值的修改ide

volatile:併發編程中,一個線程修改了某個共享變量值,其餘線程會馬上感知到這個變量值的變化。volatile只是保證數據可見性,有些人說他是一個輕量級鎖,實際上是打錯特錯的。性能

2.cpu多節緩存模型

理想中的cpu模型:
image.png
假如咱們的cpu內存模型如上:第二個cpu進行數據的讀寫,第一個cpu進行數據讀取,每次第二個cpu會先從主存讀取數據,而後進行寫操做,將不會存在數據不一致問題,由於主內存裏面的數據都是最終的數據。每次cpu1都是讀取的主內存裏面數據。每次讀取時候,只要主內存裏面數據更改了,cpu1就可以讀取到最新的值。 spa

現代計算機cpu內存模型因爲:內存的讀取速度跟不上cpu的處理速度。因此引出了;內存的讀寫速度沒什麼突破,cpu若是要頻繁的讀寫主內存的話,會致使性能較差,計算性能就會低。因此引出了:cpu多級緩存模型: 線程

image.png
一、如今計算機爲了平衡主內存跟cpu處理速度協調問題,在其之間增長了多級cpu緩存。
二、cpu能夠直接操做本身對應的cpu緩存,不須要直接頻繁的跟主內存通訊,這個是現代計算機技術的一個進步,這樣能夠保證cpu的計算的效率很是的高。
三、這樣的cpu多級緩存模型將會致使主內存裏面的實時數據在其餘cpu線程裏面讀取不到,會有一個延遲。

cpu多級緩存致使數據不一致問題的分析。
image.png

3.總線加鎖機制和MESI緩存一致性原理

早期數據不一致問題使用總線加鎖機制保證。
總線加鎖機制:
原理: 某個線程修改主內存裏面共享數據的時候,會經過一個總線,將共享數據加鎖,而後這個線程執行完相應操做以後纔會釋放鎖。
問題: 效率低下:總線加鎖機制使得多線程串行化執行,多線程下效率低下。

目前比較經常使用的是緩存一致性原理:MESI
MESI緩存一致性原理:
原理: 強制刷新主內存+cpu嗅探機制:某一個線程修改某一個數值時候,會將此數值強制刷新到主內存,而後使用cpu的嗅探機制發佈一個修改數據的事件,而後其餘線程嗅探到此數據被修改,而後使本地cpu緩存數據過時,而後把從主內存裏面從新加載。
底層原理: lock前綴指令+內存屏障

相關文章
相關標籤/搜索