成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

硬件內(nèi)存模型到 Java 內(nèi)存模型,這些硬核知識(shí)你知多少?

開發(fā) 后端
Java 內(nèi)存模型跟上一篇 JVM 內(nèi)存結(jié)構(gòu)很像,我經(jīng)常會(huì)把他們搞混,但其實(shí)它們不是一回事,而且相差還很大的,希望你沒它們搞混,特別是在面試的時(shí)候,搞混了的話就會(huì)答非所問,影響你的面試成績,當(dāng)然也許你碰到了半吊子面試官,那就要恭喜你了。

 Java 內(nèi)存模型跟上一篇 JVM 內(nèi)存結(jié)構(gòu)很像,我經(jīng)常會(huì)把他們搞混,但其實(shí)它們不是一回事,而且相差還很大的,希望你沒它們搞混,特別是在面試的時(shí)候,搞混了的話就會(huì)答非所問,影響你的面試成績,當(dāng)然也許你碰到了半吊子面試官,那就要恭喜你了。Java 內(nèi)存模型比 JVM 內(nèi)存結(jié)構(gòu)復(fù)雜很多,Java 內(nèi)存模型有一個(gè)規(guī)范叫:《JSR 133 :Java 內(nèi)存模型與線程規(guī)范》,里面的內(nèi)容很豐富,如果你沒看過的話,我建議你看一下。今天我們就簡單的來聊一聊 Java 內(nèi)存模型,關(guān)于 Java 內(nèi)存模型,我們還是先從硬件內(nèi)存模型入手。 

[[281757]]

硬件內(nèi)存模型

先來看看硬件內(nèi)存簡單架構(gòu),如下圖所示: 

硬件內(nèi)存結(jié)構(gòu) 

這是一幅簡單的硬件內(nèi)存結(jié)構(gòu)圖,真實(shí)的結(jié)構(gòu)圖要比這復(fù)雜很多,特別是在緩存層,現(xiàn)在的計(jì)算機(jī)中 CPU 緩存一般有三層,你也可以打開你的電腦看看,打開 任務(wù)資源管理器 ---> 性能 ---> cpu ,如下圖所示:

  

CPU 緩存

從圖中可以看出我這臺(tái)機(jī)器的 CPU 有三級(jí)緩存,一級(jí)緩存 (L1) 、二級(jí)緩存(L2)、三級(jí)緩存(L3),一級(jí)緩存是最接近 CPU 的,三級(jí)緩存是最接近內(nèi)存的,每一級(jí)緩存的數(shù)據(jù)都是下一級(jí)緩存的一部分。三級(jí)緩存架構(gòu)如下圖所示:

  

圖片來源網(wǎng)絡(luò)

現(xiàn)在我們對(duì)硬件內(nèi)存架構(gòu)有了一定的了解,我們來弄明白一個(gè)問題,為什么需要在 CPU 和內(nèi)存之間添加緩存?

關(guān)于這個(gè)問題我們就簡單點(diǎn)說,我們知道 CPU 是高速的,而內(nèi)存相對(duì)來說是低速的,這就會(huì)造成一個(gè)問題,不能充分的利用 CPU 高速的特點(diǎn),因?yàn)?CPU 每次從內(nèi)存里獲取數(shù)據(jù)的話都需要等待,這樣就浪費(fèi)了 CPU 高速的性能,緩存的出現(xiàn)就是用來消除 CPU 與內(nèi)存之間差距的。緩存的速度要大于內(nèi)存小于 CPU ,加入緩存之后,CPU 直接從緩存中讀取數(shù)據(jù),因?yàn)榫彺孢€是比較快的,所以這樣就充分利用了 CPU 高速的特性。但也不是每次都能從緩存中讀取到數(shù)據(jù),這個(gè)跟我們項(xiàng)目中使用的 redis 等緩存工具一樣,也存在一個(gè)緩存命中率,在 CPU 中,先查找 L1 Cache,如果 L1 Cache 沒有命中,就往 L2 Cache 里繼續(xù)找,依此類推,最后沒找到的話直接從內(nèi)存中取,然后添加到緩存中。當(dāng)然當(dāng) CPU 需要寫數(shù)據(jù)到主存時(shí),同樣會(huì)先刷新寄存器中的數(shù)據(jù)到 CPU 緩存,然后再把數(shù)據(jù)刷新到主內(nèi)存中。

也許你已經(jīng)看出了這個(gè)框架的弊端,在單核時(shí)代只有一個(gè)處理器核心,讀/寫操作完全都是由單核完成,沒什么問題;但是多核架構(gòu),一個(gè)核修改主存后,其他核心并不知道數(shù)據(jù)已經(jīng)失效,繼續(xù)傻傻的使用主存或者自己緩存層的數(shù)據(jù),那么就會(huì)導(dǎo)致數(shù)據(jù)不一致的情況。關(guān)于這個(gè)問題 CPU 硬件廠商也提供了解決辦法,叫做緩存一致性協(xié)議(MESI 協(xié)議),緩存一致性協(xié)議這東西我也不了解,我也說不清,所以就不在這里 BB 了,有興趣的可以自行研究。

聊完了硬件內(nèi)存架構(gòu),我們將焦點(diǎn)回到我們的主題 Java 內(nèi)存模型上,下面就一起來聊一聊 Java 內(nèi)存模型。

Java 內(nèi)存模型

Java 內(nèi)存模型是什么?Java 內(nèi)存模型可以理解為遵照多核硬件架構(gòu)的設(shè)計(jì),用 Java 實(shí)現(xiàn)了一套 JVM 層面的“緩存一致性”,這樣就可以規(guī)避 CPU 硬件廠商的標(biāo)準(zhǔn)不一樣帶來的風(fēng)險(xiǎn)。好了,正式介紹一下 Java 內(nèi)存模型:Java 內(nèi)存模型 ( Java Memory Model,簡稱 JMM ),本身是種抽象的概念,并不是像硬件架構(gòu)一樣真實(shí)存在的,它描述的是一組規(guī)則或規(guī)范,通過這組規(guī)范定義了程序中各個(gè)變量 (包括實(shí)例字段、靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素) 的訪問方式,更多關(guān)于 Java 內(nèi)存模型知識(shí)可以閱讀 JSR 133 :Java 內(nèi)存模型與線程規(guī)范。

我們知道 JVM 運(yùn)行程序的實(shí)體是線程,在上一篇 JVM 內(nèi)存結(jié)構(gòu)中我們得知每個(gè)線程創(chuàng)建時(shí),JVM 都會(huì)為其創(chuàng)建一個(gè)工作內(nèi)存 ( Java 棧 ),用于存儲(chǔ)線程私有數(shù)據(jù),而 Java 內(nèi)存模型中規(guī)定所有變量都存儲(chǔ)在主內(nèi)存,主內(nèi)存是共享內(nèi)存區(qū)域,所有線程都可以訪問,但線程對(duì)變量的操作 ( 讀取賦值等 ) 必須在工作內(nèi)存中進(jìn)行,首先要將變量從主內(nèi)存拷貝到自己的工作內(nèi)存空間,然后對(duì)變量進(jìn)行操作,操作完后再將變量寫回主內(nèi)存,不能直接操作主內(nèi)存中的變量。

我們知道 Java 棧是每個(gè)線程私有的數(shù)據(jù)區(qū)域,別的線程無法訪問到不同線程的私有數(shù)據(jù),所以線程需要通信的話,就必須通過主內(nèi)存來完成,Java 內(nèi)存模型就是夾在這兩者之間的一組規(guī)范,我們先來看看這個(gè)抽象架構(gòu)圖:

圖片來源網(wǎng)絡(luò)

從結(jié)構(gòu)圖來看,如果線程 A 與線程 B 之間需要通信的話,必須要經(jīng)歷下面 2 個(gè)步驟:

  1. 首先,線程 A 把本地內(nèi)存 A 中的共享變量副本中的值刷新到主內(nèi)存中去。
  2. 然后,線程 B 到主內(nèi)存中去讀取線程 A 更新之后的值,這樣線程 A 中的變量值就到了線程 B 中。

我們來看一個(gè)具體的例子來加深一下理解,看下面這張圖:

圖片來源網(wǎng)絡(luò) 

現(xiàn)在線程 A 需要和線程 B 通信,我們已經(jīng)知道線程之間通信的兩部曲了,假設(shè)初始時(shí),這三個(gè)內(nèi)存中的 x 值都為 0。線程 A 在執(zhí)行時(shí),把更新后的 x 值(假設(shè)值為 1)臨時(shí)存放在自己的本地內(nèi)存 A 中。當(dāng)線程 A 和線程 B 需要通信時(shí),線程 A 首先會(huì)把自己本地內(nèi)存中修改后的 x 值刷新到主內(nèi)存中,此時(shí)主內(nèi)存中的 x 值變?yōu)榱?1。隨后,線程 B 到主內(nèi)存中去讀取線程 A 更新后的 x 值,此時(shí)線程 B 的本地內(nèi)存的 x 值也變?yōu)榱? 1,這樣就完成了一次通信。

JMM 通過控制主內(nèi)存與每個(gè)線程的本地內(nèi)存之間的交互,來為 Java 程序員提供內(nèi)存可見性保證。Java 內(nèi)存模型除了定義了一套規(guī)范,還提供了一系列原語,封裝了底層實(shí)現(xiàn)后,供開發(fā)者直接使用。這套實(shí)現(xiàn)也就是我們常用的volatile、synchronized、final 等。

Happens-Before內(nèi)存模型

Happens-Before 內(nèi)存模型或許叫做 Happens-Before 原則更為合適,在 《JSR 133 :Java 內(nèi)存模型與線程規(guī)范》中,Happens-Before 內(nèi)存模型被定義成 Java 內(nèi)存模型近似模型,Happens-Before 原則要說明的是關(guān)于可見性的一組偏序關(guān)系。

為了方便程序員開發(fā),將底層的繁瑣細(xì)節(jié)屏蔽掉,Java 內(nèi)存模型 定義了 Happens-Before 原則。只要我們理解了 Happens-Before 原則,無需了解 JVM 底層的內(nèi)存操作,就可以解決在并發(fā)編程中遇到的變量可見性問題。JVM 定義的 Happens-Before 原則是一組偏序關(guān)系:對(duì)于兩個(gè)操作 A 和 B,這兩個(gè)操作可以在不同的線程中執(zhí)行。如果 A Happens-Before B,那么可以保證,當(dāng) A 操作執(zhí)行完后,A 操作的執(zhí)行結(jié)果對(duì) B 操作是可見的。

Happens-Before 原則一共包括 8 條,下面我們一起簡單的學(xué)習(xí)一下這 8 條規(guī)則。

1、程序順序規(guī)則

這條規(guī)則是指在一個(gè)線程中,按照程序順序,前面的操作 Happens-Before 于后續(xù)的任意操作。這一條規(guī)則還是非常好理解的,看下面這一段代碼

  1. class Test{ 
  2. 1   int x ; 
  3. 2   int y ; 
  4. 3   public void run(){ 
  5. 4       y = 20; 
  6. 5       x = 12; 
  7.     } 

第四行代碼要 Happens-Before 于第五行代碼,也就是按照代碼的順序來。

2、鎖定規(guī)則

這條規(guī)則是指對(duì)一個(gè)鎖的解鎖 Happens-Before 于后續(xù)對(duì)這個(gè)鎖的加鎖。例如下面的代碼,在進(jìn)入同步塊之前,會(huì)自動(dòng)加鎖,而在代碼塊執(zhí)行完會(huì)自動(dòng)釋放鎖,加鎖以及釋放鎖都是編譯器幫我們實(shí)現(xiàn)的

  1. synchronized (this) { 
  2.     // 此處自動(dòng)加鎖 
  3.     // x 是共享變量, 初始值 =10 
  4.     if (this.x < 12) { 
  5.        this.x = 12; 
  6.     } 
  7. } // 此處自動(dòng)解鎖 

對(duì)于鎖定規(guī)則可以這樣理解:假設(shè) x 的初始值是 10,線程 A 執(zhí)行完代碼塊后 x 的值會(huì)變成 12(執(zhí)行完自動(dòng)釋放鎖),線程 B 進(jìn)入代碼塊時(shí),能夠看到線程 A 對(duì) x 的寫操作,也就是線程 B 能夠看到 x==12。

3、volatile 變量規(guī)則

這條規(guī)則是指對(duì)一個(gè) volatile 變量的寫操作及這個(gè)寫操作之前的所有操作 Happens-Before 對(duì)這個(gè)變量的讀操作及這個(gè)讀操作之后的所有操作。

4、線程啟動(dòng)規(guī)則

這條規(guī)則是指主線程 A 啟動(dòng)子線程 B 后,子線程 B 能夠看到主線程在啟動(dòng)子線程 B 前的操作。

  1. public class Demo { 
  2.     private static int count = 0; 
  3.     public static void main(String[] args) throws InterruptedException { 
  4.         Thread t1 = new Thread(() -> { 
  5.             System.out.println(count); 
  6.         }); 
  7.         count = 12; 
  8.         t1.start(); 
  9.     } 

子線程 t1 能夠看見主線程對(duì) count 變量的修改,所以在線程中打印出來的是 12 。這也就是線程啟動(dòng)規(guī)則

5、線程結(jié)束規(guī)則

這條是關(guān)于線程等待的。它是指主線程 A 等待子線程 B 完成(主線程 A 通過調(diào)用子線程 B 的 join() 方法實(shí)現(xiàn)),當(dāng)子線程 B 完成后(主線程 A 中 join() 方法返回),主線程能夠看到子線程的操作。當(dāng)然所謂的“看到”,指的是對(duì)共享變量的操作。

  1. public class Demo { 
  2.     private static int count = 0; 
  3.     public static void main(String[] args) throws InterruptedException { 
  4.         Thread t1 = new Thread(() -> { 
  5.             // t1 線程修改了變量 
  6.             count = 12; 
  7.         }); 
  8.         t1.start(); 
  9.         t1.join(); 
  10.         // mian 線程可以看到 t1 線程改修后的變量 
  11.         System.out.println(count); 
  12.     } 

6、中斷規(guī)則

一個(gè)線程在另一個(gè)線程上調(diào)用 interrupt ,Happens-Before 被中斷線程檢測(cè)到 interrupt 被調(diào)用。

  1. public class Demo { 
  2.     private static int count = 0; 
  3.     public static void main(String[] args) throws InterruptedException { 
  4.         Thread t1 = new Thread(() -> { 
  5.             // t1 線程可以看到被中斷前的數(shù)據(jù) 
  6.             System.out.println(count); 
  7.         }); 
  8.         t1.start(); 
  9.         count = 25; 
  10.         // t1 線程被中斷 
  11.         t1.interrupt(); 
  12.     } 

mian 線程中調(diào)用了 t1 線程的 interrupt() 方法,mian 對(duì) count 的修改對(duì) t1 線程是可見的。

7、終結(jié)器規(guī)則

一個(gè)對(duì)象的構(gòu)造函數(shù)執(zhí)行結(jié)束 Happens-Before 它的 finalize()方法的開始。“結(jié)束”和“開始”表明在時(shí)間上,一個(gè)對(duì)象的構(gòu)造函數(shù)必須在它的 finalize()方法調(diào)用時(shí)執(zhí)行完。根據(jù)這條原則,可以確保在對(duì)象的 finalize 方法執(zhí)行時(shí),該對(duì)象的所有 field 字段值都是可見的。

8、傳遞性規(guī)則

這條規(guī)則是指如果 A Happens-Before B,且 B Happens-Before C,那么 A Happens- Before C。

責(zé)任編輯:華軒 來源: 平頭哥的技術(shù)博文
相關(guān)推薦

2024-07-26 10:23:52

2025-02-20 14:52:02

2011-08-05 15:32:44

2018-07-04 14:43:55

對(duì)象模型內(nèi)存結(jié)構(gòu)內(nèi)存模型

2009-06-24 16:50:11

Java內(nèi)存模型

2018-11-20 09:37:19

Java內(nèi)存模型

2025-06-23 10:08:22

2024-06-18 14:01:17

2023-07-11 08:43:43

volatileJava內(nèi)存

2025-06-06 06:25:07

2020-06-28 11:44:02

IO模型計(jì)算機(jī)

2025-03-04 10:45:19

JVM內(nèi)存模型Java

2010-09-25 12:38:40

JVM內(nèi)存模型

2018-12-18 14:08:01

Java內(nèi)存volatile

2013-07-11 10:37:20

Java內(nèi)存模型

2022-07-07 08:00:51

Java內(nèi)存模型

2020-12-07 06:23:48

Java內(nèi)存

2022-06-30 08:52:33

GoC++內(nèi)存模型

2016-01-28 19:58:43

創(chuàng)業(yè)IT建設(shè)

2020-06-08 07:00:00

數(shù)據(jù)安全加密機(jī)密計(jì)算
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 美女在线观看av | 玖玖爱365 | 亚洲另类视频 | 一级毛片免费完整视频 | 欧美一区二区在线观看 | 97人澡人人添人人爽欧美 | 国产婷婷色综合av蜜臀av | 国产精品久久二区 | 91精品国产91久久综合桃花 | 中文字幕日韩一区 | 国产一区二区三区视频 | 日韩一区二区免费视频 | 日韩精品成人 | 成人在线免费观看视频 | 国产aaaaav久久久一区二区 | 福利社午夜影院 | 精品视频免费 | 操射视频| 色婷婷av一区二区三区软件 | 欧美久久久久久久 | 午夜视频一区二区三区 | 亚洲免费在线观看 | 91网站在线看 | 精品一区二区久久 | 99精品99久久久久久宅男 | 极情综合网 | 99久久夜色精品国产亚洲96 | 久久久九九九九 | 国产精品美女视频 | 日韩国产欧美在线观看 | 狠狠爱视频 | 国产亚洲一区二区三区 | 中国黄色毛片视频 | 久久精品国产99国产精品 | 久久天天 | 亚洲色综合| 久久精品91| 亚洲综合成人网 | 超碰免费在线 | 91啪影院 | 天天干b|