高并發編程-通過volatile重新認識CPU緩存 和 Java內存模型(JMM)

      網友投稿 764 2025-04-01

      文章目錄

      概述

      volatile定義

      CPU緩存

      相關CPU術語

      CPU緩存一致性協議MESI

      帶有高速緩存的CPU執行計算的流程

      CPU 多級的緩存結構

      Java 內存模型 (JMM)

      線程通信的兩種方式

      哪些變量可以共享

      JMM概述

      Java內存模型的抽象結構示意圖

      volatile 小demo

      總結:volatile的兩條實現原則

      概述

      在多線程并發編程中synchronized和volatile都扮演著重要的角色。

      volatile是輕量級的 synchronized,它在高并發中保證了共享變量的“可見性”。

      那什么是可見性呢?

      可見性 我們可以理解為:當一個線修改一個共享變量時,另外一個線程能讀到這個修改的值。

      如果volatile變量修飾符使用恰的話,它比synchronized的使用和執行成本更低,因為

      volatile不會引起線程上下文的切換和調度

      volatile定義

      Java規范第3版中對volatile的定義如下:

      Java允許線程訪問共享變量,為了確保共享變量能被準確和一致地更新,線程應該確保通過排他鎖單獨獲得這個變量。

      Java提供了volatile關鍵字,在某些場景下volatile比鎖synchronized要更加方便。

      如果一個字段被聲明成volatile,Java線程內存模型(JMM)確保所有線程看到這個變量的值是一致的 .

      CPU緩存

      相關CPU術語

      了解volatile實現原理之前,先了解下與其實現原理相關的CPU術語

      CPU緩存一致性協議MESI

      CPU緩存一致性協議MESI 請參考: CPU緩存一致性協議MESI

      【M 修改 (Modified) E 獨享、互斥 (Exclusive) S 共享 (Shared) I 無效 (Invalid) 】

      CPU的發展速度非常快,而內存和硬盤的發展速度遠遠不及CPU。這就造成了高性能能的內存和硬盤價格及其昂貴。然而CPU的高度運算需要高速的數據。為了解決這個問題,CPU廠商在CPU中內置了少量的高速緩存以解決I\O速度和CPU運算速度之間的不匹配問題

      為了提高效率,CPU不直接和內存進行通信,而是先將系統內存的數據讀取到內部緩存(L1、L2或其他)后再進行操作。

      但是有個問題: 當操作完成后,被修改的數據何時回寫到主內存呢?

      假設某個共享變量聲明了volatile關鍵字進行寫操作 ,JVM就會向處理器發送一條Lock前綴指令,將這個變量所在緩存行的數據寫回到系統內存。

      OK,就算寫回到內存,如果其他處理器緩存的值還是舊的,再執行計算操作就會有問題。

      所以,在多處理器下,為了保證各個處理器的緩存是一致的,就要實現緩存一致性協議 ,每個處理器通過嗅探在總線(BUS)上傳播的數據來檢查自己緩存的值是不是過期了

      當處理器發現自己緩存行對應的內存地址被修改,就會將當前處理器的緩存行設置成無效狀態,

      當處理器對這個數據進行修改操作的時候,會重新從系統內存中把數據讀到處理器緩存

      帶有高速緩存的CPU執行計算的流程

      程序以及數據被加載到主內存

      指令和數據被加載到CPU的高速緩存

      CPU執行指令,把結果寫到高速緩存

      高速緩存中的數據寫回主內存

      CPU 多級的緩存結構

      由于CPU的運算速度超越了1級緩存的數據I\O能力,CPU廠商又引入了多級的緩存結構。

      L1/L2/L3 Cache速度差別

      L1 cache: 3 cycles

      L2 cache: 11 cycles

      L3 cache: 25 cycles

      Main Memory: 100 cycles

      通常L1 Cache離CPU核心需要數據的地方更近,而L2 Cache則處于邊緩位置,訪問數據時,L2 Cache需要通過更遠的銅線,甚至更多的電路,從而增加了延時。

      參見: 細說Cache-L1/L2/L3/TLB

      Java 內存模型 (JMM)

      線程通信的兩種方式

      我們知道 線程間的通信,主要分為兩種方式

      共享內存 (線程之間共享程序的公共狀態,通過寫-讀內存中的公共狀態進行隱式通信)

      消息傳遞 (線程之間必須通過發送消息來顯式進行通信)

      哪些變量可以共享

      Java的并發采用的是共享內存模型 , 在Java中,所有實例域、靜態域和數組元素都存儲在堆內存中,堆內存在線程之間共享 ,我們使用”共享變量”這個術語代指實例域,靜態域和數組元素

      局部變量,方法定義參數和異常處理器參數不會在線程之間共享,它們不會有內存可見性問題,也不受內存模型的影響。

      JMM概述

      Java線程之間的通信由Java內存模型JMM)控制,JMM決定一個線程對共享變量的寫入何時對另一個線程可見 .

      JMM定義了線程和主內存之間抽象關系:線程之間的共享變量存儲在主內存(Main Memory)中,每個線程都有一個私有的本地內存(Local Memory),本地內存中存儲了該線程以讀/寫共享變量的副本。

      注: 本地內存是JMM的一個抽象概念,并不真實存在。它涵蓋了緩存、寫緩沖區、寄存器以及其他的硬件和編譯器化

      Java內存模型的抽象結構示意圖

      如下:

      根據上述的描述,如果線程A和線程B要通信的話,步驟如下

      線程A把本地內存A中更新過的共享變量刷新到主內存中去

      線程B到主內存中去讀取線程A之前已更新過的共享變

      線程A和線程B通信示意圖如下所示

      本地內存A和本地內存B由主內存中共享變量x的副本。

      假設初始時,這3個內存(本地內存A、本地內存B、主內存)中的x值都為0。

      線程A在執行時,把更新后的x值(假設值為1)臨時存放在自己的本地內A中。

      當線程A和線程B需要通信時,線程A首先會把自己本地內存中修改后的x值刷新到主內存中,此時主內存中的x值變為了1。

      隨后,線程B到主內存中去讀取線程A更新后的x值,此時線程B的本地內存的x值也變為了1。

      從整體來看,這兩個步驟實質上是線程A在向線程B發送消息,而且這個

      通信過程必須要經過主內存。

      JMM通過控制主內存與每個線程的本地內存之間的交互,來保證內存可見性保證。

      volatile 小demo

      先來個例子 感受下volatile的作用

      倆線程 1個讀取共享變量 另外1個更新共享變量.

      package com.artisan.test; /** * 倆線程 *

      * 1個讀取共享變量 * 1個更新共享變量 */ public class VolatileDemo { // 共享變量 private volatile static int SHARED_VALUE = 0; private final static int MAX_VALUE = 10; public static void main(String[] args) { // 定義 讀取線程 new Thread(() -> { int localValue = SHARED_VALUE; // 循環, 如果localValue != SHARED_VALUE 輸出信息 while (SHARED_VALUE < MAX_VALUE){ if (localValue != SHARED_VALUE){ System.out.printf(Thread.currentThread().getName() + ": the SHARED_VALUE value has been updated to [%d] \n" , SHARED_VALUE); localValue = SHARED_VALUE; } } }, "Reader Thread").start(); // 定義 更新線程 new Thread(() -> { int localValue = SHARED_VALUE; // 循環 如果小于最大值,則更新localValue while (SHARED_VALUE < MAX_VALUE){ System.out.printf(Thread.currentThread().getName() + ": update SHARED_VALUE to [%d] \n" , ++localValue); SHARED_VALUE = localValue; try { // 為了演示效果,休眠一下 Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }, "Update Thread").start(); } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      高并發編程-通過volatile重新認識CPU緩存 和 Java內存模型(JMM)

      輸出:

      Update Thread: update SHARED_VALUE to [1] Reader Thread: the SHARED_VALUE value has been updated to [1] Update Thread: update SHARED_VALUE to [2] Reader Thread: the SHARED_VALUE value has been updated to [2] Update Thread: update SHARED_VALUE to [3] Reader Thread: the SHARED_VALUE value has been updated to [3] Update Thread: update SHARED_VALUE to [4] Reader Thread: the SHARED_VALUE value has been updated to [4] Update Thread: update SHARED_VALUE to [5] Reader Thread: the SHARED_VALUE value has been updated to [5] Update Thread: update SHARED_VALUE to [6] Reader Thread: the SHARED_VALUE value has been updated to [6] Update Thread: update SHARED_VALUE to [7] Reader Thread: the SHARED_VALUE value has been updated to [7] Update Thread: update SHARED_VALUE to [8] Reader Thread: the SHARED_VALUE value has been updated to [8] Update Thread: update SHARED_VALUE to [9] Reader Thread: the SHARED_VALUE value has been updated to [9] Update Thread: update SHARED_VALUE to [10] Process finished with exit code 0

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      如果 去掉volatile關鍵字呢 ? 測試如下

      由此可見 volatile關鍵字在高并發中保證了共享變量的“可見性”。

      總結:volatile的兩條實現原則

      總結一下

      Lock前綴指令會引起處理器緩存回寫到內存

      一個處理器的緩存回寫到內存會導致其他處理器的緩存無效

      Java 任務調度

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:如何將標題返回表頭(怎么使表格標題總出現表頭)
      下一篇:vue框架學習
      相關文章
      亚洲精品456人成在线| 2020年亚洲天天爽天天噜| 亚洲欧美日韩一区二区三区| 精品亚洲成a人片在线观看少妇 | 亚洲精品天堂在线观看| 91亚洲性爱在线视频| 久久久无码精品亚洲日韩蜜臀浪潮| 亚洲国产精品嫩草影院在线观看| 亚洲精品无码Av人在线观看国产| 亚洲中文字幕无码日韩| 亚洲色偷偷综合亚洲AVYP| 亚洲乱码一区二区三区在线观看 | 亚洲人成电影在线播放| 亚洲精品97久久中文字幕无码| 亚洲高清偷拍一区二区三区| 亚洲免费在线观看| 伊伊人成亚洲综合人网7777| 亚洲日韩精品一区二区三区 | 国产∨亚洲V天堂无码久久久| 亚洲精品国产精品乱码视色| 亚洲精品tv久久久久久久久| 久久亚洲国产成人亚| 中文字幕亚洲第一在线| 亚洲精品亚洲人成在线观看麻豆| 亚洲春色另类小说| youjizz亚洲| 亚洲熟女乱色一区二区三区| 亚洲国产精华液2020| 日韩色日韩视频亚洲网站| 婷婷亚洲综合一区二区| 亚洲另类少妇17p| 日韩亚洲欧洲在线com91tv| 亚洲第一区香蕉_国产a| 亚洲精品综合久久中文字幕| 亚洲人成影院在线高清| 国产午夜亚洲精品| 亚洲国产成人久久一区WWW| 国产亚洲午夜高清国产拍精品| 国产亚洲美女精品久久久久狼| 亚洲国产综合专区在线电影| 亚洲国产福利精品一区二区|