多線程知識點分析之內存模型

一、現代計算機中的高速緩存

        在計算機組成原理中講到,現在計算機爲了匹配計算機存儲設備的讀寫速度與處理器運算速度,在cpu與內存設備

之間加入了一個名爲cache的高速緩存設備作爲緩衝:將運算需要用到的數據從內存複製到cache中,cpu可以在運行期

間對cache進行高速的讀寫操作,運行結束後再從cache把數據同步回內存。

        cache引出的一個新問題:緩存一致性。每個處理器都有自己的cache,而他們又共享一個主內存,當多個處理器的

運算任務都涉及同一內存區域時,將會導致各自的緩存數據不一致性。那麼,在各自運算結束後以誰的緩存數據爲準同

步回主內存中呢,再次就需要緩存一致性協議來制定多個處理器對統一內存區域的讀寫操作了。

        除了cache,爲了更好地利用處理器資源,處理器還會對代碼進行亂序執行,在最後才把結果重組使得結果與順序執

行一致。

二、java內存模型

      類比現代計算機中的主內存與cache,jvm中規定了所有變量都存儲在主內存中(類比計算機的內存),然後每條線程都

有自己的工作內存(類比每個處理器的cache)。線程的工作內存中保存了該線程需要用到的變量的拷貝值,線程在cpu上

運行時都是對自己工作線程中的數據進行讀寫操作,運行結束之後才把數據同步化到主內存中去。那麼類比於計算機使

用緩存一致性協議,解決緩存一致性問題,jvm中就需要線程同步機制來達到多線程對同一內存區域的讀寫控制了。

     另外,java編譯器爲了提高性能,採取了指令重排序(類比計算機的亂序執行),若多個線程都有語句對同一內存區域

進行操作的話,有可能因爲指令重排序而導致結果不符預料。因此,也需要線程同步機制來達到多線程對同一內存區域

的讀寫控制。

三、主內存與工作內存的數據交互

jvm定義了8種操作來完成主內存與線程工作內存的數據交互:

1.lock:把主內存變量標識爲一條線程獨佔,此時不允許其他線程對此變量進行讀寫。

2.unlock:解鎖一個主內存變量。

3.read:把一個主內寸變量值讀入到線程的工作內存,強調的是讀入這個過程。

4.load:把read到的變量值保存到線程工作內存中作爲變量副本,強調的是讀入的值的保存過程。

5.use:線程執行期間,把工作內存中的變量值傳給字節碼執行引擎。

6.assign(賦值):字節碼執行引擎把運算結果傳回工作內存,賦值給工作內存中的結果變量。

7.store:把工作內存中的變量值傳送到主內存的變量中,強調傳送的過程。

8.write:把store傳送進來的變量值寫入主內存的變量中,強調保存的過程。

jvm要求以上八個操作都具有原子性,即對數據的讀寫操作具有原子性。但也有例外,即long,double的非原子性協定:

這個兩個64位類型的數據的讀、寫操作各需兩次進行,一次讀/寫32位,這兩次讀/兩次寫是不保證原子性的。

四、原子性、可見性、有序性

原子性:基本數據類型的讀寫操作都是原子性的;更大範圍的(代碼塊)的原子性可以用lock、unlock操作來實現(上鎖之

              後就只有一個線程來執行了,所以不會被其他線程打斷原子操作),表現到代碼層面就是使用sychronized同步

              塊。

可見性:當一個線程修改了被多線程共享的一個主內存變量值時,其他線程能立刻知道這個修改。

              我們在上面已經知道,jvm是通過工作內存中的變量值變化後,把新值同步回主內存,然後其他線程從主內存

              讀取這個新值來實現可見性的。這裏有個區別:普通變量的值變化後不一定會立刻同步回主內存中,而是會等

              線程執行完成或者等待一段時間之後才同步回,而且同步回主內存之後其他線程也不一定會立刻讀取新值,而

              被voliate關鍵字修飾的變量,一旦在工作內存中被修改則立刻同步回主內存,並且其他使用了該變量的線程的

              工作內存也會立刻從主內存中讀取新值。而syncrhoized關鍵字修飾的變量由於一次只能有一個線程能使用,故

              一次也只能有一個工作線程讀寫它,所以也能“縱向”地實現可見性。

有序性:多線程之間對共享數據的操作的有序性,可以通過volatile和syncrhoized關鍵字來保證。volatile關鍵字禁止了指

              令重排序,而syncrhoized關鍵字規定了多個線程每次只能有一個線程對共享數據進行操作。

五、volatile

            其實道理上講同一實例的同一屬性本身只有一個副本。但是多線程是會緩存值的,本質上,volatile就是不去緩存,

直接取值。在線程安全的情況下加volatile會犧牲性能。

             volatile變量具有兩種特性:可見性、禁止指令重排序。但是,volatile不具備原子性,因爲volatile變量的值可以被

多線程交替修改,而修改包括了read、load、use、store、write等過程,這些過程不能保證是原子執行的。

           可見性:被volatile關鍵字修飾的變量,一旦在工作內存中被修改,則立刻同步回主內存,並且其他使用了這個變量

                          的線程的工作內存會立刻從主內存讀取新值。

          禁止指令重排序:volatile變量在賦值後會創建一個內存屏障:指令重排序時,位於後面的指令不能排到內存屏障之前。

           使用條件:

                           ① 對變量的寫操作不依賴於當前值

                           ② 該變量沒有包含在具體其他變量的不變式中
                           這些條件表明,可以被寫入 volatile 變量的這些有效值獨立於任何程序的狀態,包括變量的當前狀態。

                           第一個條件的限制使volatile變量不能用作線程安全計算器。雖然x++是一個簡單的操作,但是實際上它是

                           一個由讀取-修改-寫入操作序列組成的組合操作,volatie對於操作不具有原子性,所以volatile不能保證在

                           x的值在操作期間保持不變。
          使用場景:volatile一個使用場景是狀態位;還有隻有一個線程寫,其餘線程讀的場景

          與sychronized的比較:

          volatile是輕量級鎖,sychronized是重量級鎖,且volatile不保證原子性,而sychronized保證原子性。

   

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