真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java內(nèi)存模型的知識點(diǎn)有哪些

這篇文章主要介紹“Java內(nèi)存模型的知識點(diǎn)有哪些”,在日常操作中,相信很多人在Java內(nèi)存模型的知識點(diǎn)有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java內(nèi)存模型的知識點(diǎn)有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

創(chuàng)新互聯(lián)建站堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:網(wǎng)站制作、網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時代的龍湖網(wǎng)站設(shè)計(jì)、移動媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

關(guān)于 Java  內(nèi)存模型,我們還是先從硬件內(nèi)存模型入手。

硬件內(nèi)存模型

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

Java內(nèi)存模型的知識點(diǎn)有哪些

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

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

Java內(nèi)存模型的知識點(diǎn)有哪些 

CPU 緩存

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

Java內(nèi)存模型的知識點(diǎn)有哪些 

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

關(guān)于這個問題我們就簡單點(diǎn)說,我們知道 CPU 是高速的,而內(nèi)存相對來說是低速的,這就會造成一個問題,不能充分的利用 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ù),這個跟我們項(xiàng)目中使用的 redis 等緩存工具一樣,也存在一個緩存命中率,在 CPU 中,先查找 L1  Cache,如果 L1 Cache 沒有命中,就往 L2 Cache 里繼續(xù)找,依此類推,最后沒找到的話直接從內(nèi)存中取,然后添加到緩存中。當(dāng)然當(dāng) CPU  需要寫數(shù)據(jù)到主存時,同樣會先刷新寄存器中的數(shù)據(jù)到 CPU 緩存,然后再把數(shù)據(jù)刷新到主內(nèi)存中。

也許你已經(jīng)看出了這個框架的弊端,在單核時代只有一個處理器核心,讀/寫操作完全都是由單核完成,沒什么問題;但是多核架構(gòu),一個核修改主存后,其他核心并不知道數(shù)據(jù)已經(jīng)失效,繼續(xù)傻傻的使用主存或者自己緩存層的數(shù)據(jù),那么就會導(dǎo)致數(shù)據(jù)不一致的情況。關(guān)于這個問題  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ī)范定義了程序中各個變量  (包括實(shí)例字段、靜態(tài)字段和構(gòu)成數(shù)組對象的元素) 的訪問方式,更多關(guān)于 Java 內(nèi)存模型知識可以閱讀 JSR 133 :Java 內(nèi)存模型與線程規(guī)范。

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

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

Java內(nèi)存模型的知識點(diǎn)有哪些

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

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

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 首先,線程 A 把本地內(nèi)存 A 中的共享變量副本中的值刷新到主內(nèi)存中去。

  3. 然后,線程 B 到主內(nèi)存中去讀取線程 A 更新之后的值,這樣線程 A 中的變量值就到了線程 B 中。

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

Java內(nèi)存模型的知識點(diǎn)有哪些

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

JMM 通過控制主內(nèi)存與每個線程的本地內(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)系:對于兩個操作 A 和 B,這兩個操作可以在不同的線程中執(zhí)行。如果 A Happens-Before B,那么可以保證,當(dāng) A  操作執(zhí)行完后,A 操作的執(zhí)行結(jié)果對 B 操作是可見的。

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

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

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

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

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

2、鎖定規(guī)則

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

synchronized (this) {     // 此處自動加鎖     // x 是共享變量, 初始值 =10     if (this.x < 12) {        this.x = 12;     } } // 此處自動解鎖

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

3、volatile 變量規(guī)則

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

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

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

public class Demo {     private static int count = 0;     public static void main(String[] args) throws InterruptedException {         Thread t1 = new Thread(() -> {             System.out.println(count);         });         count = 12;         t1.start();     } }

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

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

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

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

6、中斷規(guī)則

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

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

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

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

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

8、傳遞性規(guī)則

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

到此,關(guān)于“Java內(nèi)存模型的知識點(diǎn)有哪些”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!


新聞名稱:Java內(nèi)存模型的知識點(diǎn)有哪些
網(wǎng)站地址:http://weahome.cn/article/jesocc.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部