日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

并发编程-02并发基础CPU多级缓存和Java内存模型JMM

發(fā)布時間:2025/3/21 java 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 并发编程-02并发基础CPU多级缓存和Java内存模型JMM 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • CPU多級緩存
    • CPU多級緩存概述
    • CPU 多級緩存-緩存一致性協(xié)議MESI
    • CPU 多級緩存-亂序執(zhí)行優(yōu)化-重排序
  • JAVA內存模型 (JMM)
      • 計算機硬件架構簡易圖示
      • JAVA內存模型與硬件架構之間的關系
      • Java內存模型的抽象結構
      • Java內存模型的同步八種操作
      • Java內存模型 - 同步規(guī)則
  • 并發(fā)編程優(yōu)缺點
  • 代碼

CPU多級緩存

CPU多級緩存概述

為什么CPU緩存會分為一級緩存L1、L2、L3?有什么意義?

CPU的頻率非常快,主存Main Memory跟不上。CPU緩存是CPU與內存之間的臨時數(shù)據(jù)交換器,為了解決CPU運行處理速度與內存讀寫速度不匹配的矛盾——緩存的速度比內存的速度快多了。

上圖左側為簡易的高速緩存結構,數(shù)據(jù)的讀取和存儲都經(jīng)過高速緩存Cache,CPU核心與高速緩存有一條特殊的快速通道;主存Main Memory與高速緩存都連在系統(tǒng)總線上(BUS)這條總線還用于其它組件的通信。

在高速緩存出現(xiàn)后不久,系統(tǒng)變得愈加復雜,高速緩存與主存之間的速度差異被拉大,直到加入了L2 Cache ,甚至L3 Cache。它們的作用都是

  • 作為CPU與主內存之間的高速數(shù)據(jù)緩沖區(qū),L1最靠近CPU核心;L2其次;L3再次。
  • 運行速度方面:L1>L2>L3
  • 容量大小方面:L1<L2<L3

CPU會先在最快的L1中尋找需要的數(shù)據(jù),找不到再去找次快的L2,還找不到再去找L3,L3都沒有那就只能去內存找了。如上圖右側。


CPU 多級緩存-緩存一致性協(xié)議MESI

MESI協(xié)議的作用:用于保證多個CPU Cache之間緩存共享數(shù)據(jù)的一致

MESI 是指4中狀態(tài)的首字母。每個Cache line有4個狀態(tài),可用2個bit表示,它們分別是:

注: 緩存行(Cache line):緩存存儲數(shù)據(jù)的單元。

狀態(tài)描述監(jiān)聽任務
M 修改 (Modified)該Cache line有效,數(shù)據(jù)被修改了,和內存中的數(shù)據(jù)不一致,數(shù)據(jù)只存在于本Cache中緩存行必須時刻監(jiān)聽所有試圖讀該緩存行相對就主存的操作,這種操作必須在緩存將該緩存行寫回主存并將狀態(tài)變成S(共享)狀態(tài)之前被延遲執(zhí)行
E 獨享、互斥 (Exclusive)該Cache line有效,數(shù)據(jù)和內存中的數(shù)據(jù)一致,數(shù)據(jù)只存在于本Cache中緩存行也必須監(jiān)聽其它緩存讀主存中該緩存行的操作,一旦有這種操作,該緩存行需要變成S(共享)狀態(tài)
S 共享 (Shared)該Cache line有效,數(shù)據(jù)和內存中的數(shù)據(jù)一致,數(shù)據(jù)存在于很多Cache中緩存行也必須監(jiān)聽其它緩存使該緩存行無效或者獨享該緩存行的請求,并將該緩存行變成無效(Invalid)
I 無效 (Invalid)該Cache line無效

觸發(fā)事件:

觸發(fā)事件描述
本地讀取(Local read)本地cache讀取本地cache數(shù)據(jù)
本地寫入(Local write)本地cache寫入本地cache數(shù)據(jù)
遠端讀取(Remote read)其它cache讀取本地cache數(shù)據(jù)
遠端寫入(Remote write)其它cache寫入本地cache數(shù)據(jù)

CPU 多級緩存-亂序執(zhí)行優(yōu)化-重排序

處理器為提高運算速度而做出違背代碼原有順序的優(yōu)化, 線程下的情況下可見性就會出現(xiàn)問題。

在正常情況下是不對結果造成影響的。在單核時代處理器對結果的優(yōu)化保證不會遠離預期目標,但是在多核環(huán)境下卻并非如此. 因為在多核條件下會有多個核執(zhí)行指令,因此每個核的指令都有可能會亂序。另外處理器還引入了L1、L2緩存機制,這就導致了邏輯上后寫入的數(shù)據(jù)不一定最后寫入。

在執(zhí)行程序時,為了提高性能,編譯器和處理器常常會對指令做重排序。重排序分為下面三種:

  • 編譯器優(yōu)化的重排序。編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執(zhí)行順序。

  • 指令級并行的重排序。現(xiàn)代處理器采用了指令集并行技術來將多條指令重疊執(zhí)行。如果不存在數(shù)據(jù)依賴,處理器可以改變語句對應機器指令的執(zhí)行順序。

  • 內存系統(tǒng)的重排序。由于處理器使用緩存和讀/寫緩沖區(qū),這使得加載和存儲操作看上去可能可能是在亂序執(zhí)行。

  • 編譯器重排序屬于編譯器重排序,指令級并行重排序和內存系統(tǒng)重排序屬于處理器重排序,這些重排序可能會導致多線程出現(xiàn)內存可見性問題。


    JAVA內存模型 (JMM)

    上面講的是硬件CPU的多級緩存,為了屏蔽掉各種系統(tǒng)硬件和操作系統(tǒng)的內存訪問差異,以實現(xiàn)Java程序在各大平臺都能達到一致的并發(fā)效果,Java虛擬機因此定義了Java內存模型,它規(guī)范了Java虛擬機與計算機是如何協(xié)同工作的。

    接著說上面的重排序,對于JVM來講是怎樣的呢?

  • 對于編譯器,JMM的編譯器重排序規(guī)則會禁止特點類型的重排序。
  • 對于處理器重排序,JMM的處理器重排序規(guī)則會要求Java編譯器在生成指令序列時,插入特定類型的內存屏障指令,通過內存屏障指令來禁止特點類型的處理器重排序。
  • JMM屬于語言級的內存屏障,它確保在不同的編譯器和不同的處理器平臺上,通過禁止特點類型的編譯器重排序和處理器重排序,為程序員提供一致的內存可見性保證。
  • 那JMM(Java Memory Model)

    關于JVM內存區(qū)域中的堆棧請參考 JVM-01Java內存區(qū)域與內存溢出異常(上)【運行時區(qū)域數(shù)據(jù)】

    存在堆上的對象,可以被持有這個對象的引用的線程訪問。如果兩個線程同時訪問同一個對象的私有變量,此時這兩個線程所擁有的是"這個對象的私有拷貝"。 比如這里的Object3


    計算機硬件架構簡易圖示

    CPU:一個計算機一般有多個CPU,一個CPU還會有多核。因此意味著每個CPU可能都會運行一個線程,所以計算機出現(xiàn)多線程是很有可能的。

    CPU Registers(寄存器):每個CPU都包含一系列的寄存器,它們是CPU內存的基礎,CPU在寄存器上執(zhí)行的 速度遠大于在主存上執(zhí)行的速度,這是因為計算機訪問寄存器的速度遠大于主存。

    CPU Cache(高速緩存):由于計算機的存儲設備與處理器的處理設備有著幾個數(shù)量級的差距,所以現(xiàn)代計 算機都會加入一層讀寫速度與處理器處理速度接近相同的高級緩存來作為內存與處理器之間的緩沖,將運算使用到的數(shù)據(jù)復制到緩存中,讓運算能夠快速的執(zhí)行,當運算結束后,再從緩存同步到內存之中,這 樣,CPU就不需要等待緩慢的內存讀寫了主(內)存 。 一個計算機包含一個主存,所有的CPU都可以訪問主存,主存比緩存容量大的多(CPU訪問緩存層的速度快于訪問主存的速度!但通常比訪問內存寄存器的速度還是要慢點)。


    通常情況下,當一個CPU要讀取主存(RAM - Main Mernory)的時候,它首先會將主存中的數(shù)據(jù)讀 取到CPU緩存中,甚至將緩存內容讀到內部寄存器里面,然后再寄存器執(zhí)行操作,當運行結束后,會將寄存器中的值刷新回緩存中,并在某個時間點將值刷新回主存。


    JAVA內存模型與硬件架構之間的關系

    右側的硬件內存模型是沒有區(qū)分線程 Stack棧 和 Heap堆,對于硬件而言,所有的棧和堆分布在主存里面,部分棧和堆也可能出現(xiàn)在CPU緩存以及CPU內部的寄存器中。


    Java內存模型的抽象結構


    如果線程A和線程B要通信的話,必須要經(jīng)歷下面兩個步驟

  • 線程A把本地內存A中跟新過的共享變量刷新到主內存中去
  • 線程B到主內存中去讀取線程A更新過的共享變量
  • 使用如下示意圖更加清晰

    假設這3個內存中x均為0,線程A執(zhí)行時將更新后的值假設更新為1臨時存放到自己的本地內存A中。 當線程A和B需要通信時,線程A首先會把自己本地內存中的修改后的值即1刷新到主內存中,此時主內存中x=1. 隨后線程B到主內存中去讀取線程A更新后的值,此時線程B的本地內存的x值也變成了1. 【正常情況 A先B后】

    如果線A和線程B同時讀取到主內存中的x值,均為0 ,線程A將x值更新為1,放到線程A本地內存,因為線程A和線程B它們之間的數(shù)據(jù)不可見,線程B并沒有等線程A寫回主內存之后做更新操作 ,此時線程B也做了同樣的更新操作,這個時候線程B的本地內存中x也變成了1 ,因此當線程B操作完成將結果1寫回主內存時計數(shù)就出現(xiàn)了錯誤【因為線程B并沒有等線程A將更新后數(shù)據(jù)寫會主內存】,正確的情況應該是線程B讀取主內存中的1,然后更新為2,再次寫會主內存,主內存最后的x=2. 這就引起了并發(fā)問題。這就解釋了我們案例中的count為啥不總是等于1萬的情況 , 案例-> https://blog.csdn.net/yangshangwei/article/details/87400938#_48 【異常情況 AB同時執(zhí)行】

    所以需要使用同步的手段去確保程序處理的準確性。

    從整體上看,這兩個步驟實質上是線程A向線程B發(fā)送消息,而且這個通信過程必須要經(jīng)過主內存。 JMM通過控制主內存與每個線程的本地內存之間的交互,來提供內存可見性保證 。


    Java內存模型的同步八種操作

  • Lock(鎖定):作用于主內存的變量,把一個變量標識變?yōu)橐粭l線程獨占狀態(tài)

  • Unlock(解鎖):作用于主內存的變量,把一個處于鎖定狀態(tài)的變量釋放出來,釋放后的變量才可以被其它線程鎖定

  • Read(讀取):作用于主內存的變量,把一個變量值從主內存?zhèn)鬏數(shù)骄€程的工作內存中,以便隨后的load動作使用

  • Load(載入):作用于工作內存的變量,它把Read操作從主內存中得到的變量值放入工作內存的變量副本中

  • Use(使用):作用于工作內存的變量,把工作內存中的一個變量值傳遞給執(zhí)行引擎

  • Assign(賦值):作用于工作內存的變量,它把一個從執(zhí)行引擎接受到的值賦值給工作內存的變量

  • Store(存儲):作用于工作內存的變量,把工作內存中的一個變量的值傳送到主內存中,以便隨后的write的操作

  • Write(寫入):作用于主內存的變量,它把Store操作從工作內存中一個變量的值傳送到主內存的變量中


  • Java內存模型 - 同步規(guī)則

  • 如果要把一個變量從主內存中賦值到工作內存,就需要按順序得執(zhí)行read和load操作,如果把變量從工作內 存中同步回主內存中,就要按順序得執(zhí)行store和write操作,但java內存模型只要求上述操作必須按順序執(zhí)行,沒有保證必須是連續(xù)執(zhí)行,也就是說Read和Load、Store和Write之間是可以插入其它指令的
  • 不允許read和load、store和write操作之一單獨出現(xiàn)
  • 不允許一個線程丟棄它的最近assign的操作,即變量在工作內存中改變了之后必須同步到主內存中
  • 不允許一個線程無原因地(也就是說必須有assgin操作)把數(shù)據(jù)從工作內存同步到主內存中
  • 一個新的變量只能在主內存中誕生,不允許在工作內存中直接使用一個未被初始化(load或assign)的變量。即就是對一個變量實施use和store操作之前,必須先執(zhí)行過了load和assign操作
  • 一個變量在同一時刻只允許一條線程對其進行l(wèi)ock操作,但lock操作可以同時被一條線程重復執(zhí)行多次,多次執(zhí)行l(wèi)ock后,只有執(zhí)行相同次數(shù)的unlock操作,變量才會解鎖,lock和unlock必須成對出現(xiàn)
  • 如果一個變量執(zhí)行l(wèi)ock操作,將會清空工作內存中此變量的值,在執(zhí)行引擎中使用這個變量前需要重新執(zhí)行 load或assign操作初始化變量的值
  • 如果一個變量事先沒有被lock操作鎖定,則不允許它執(zhí)行unlock操作,也不允許去unlock一個被其它線程鎖定的變量
  • 對一個變量執(zhí)行unlock操作之前,必須先把此變量同步到主內存中(其實就是執(zhí)行store和write操作之后)

  • 并發(fā)編程優(yōu)缺點


    代碼

    https://github.com/yangshangwei/ConcurrencyMaster

    總結

    以上是生活随笔為你收集整理的并发编程-02并发基础CPU多级缓存和Java内存模型JMM的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。