日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

一文搞定JMM核心原理

來源: 責編: 時間:2024-01-09 08:52:17 220觀看
導讀JMM引入從堆棧說起JVM內部使用的Java內存模型在線程棧和堆之間劃分內存。此圖從邏輯角度說明了Java內存模型:圖片# 堆棧里面放了什么?線程堆棧還包含正在執行的每個方法的所有局部變量(調用堆棧上的所有方法)。線程只

JMM引入

從堆棧說起

JVM內部使用的Java內存模型在線程棧和堆之間劃分內存。此圖從邏輯角度說明了Java內存模型:6JB28資訊網——每日最新資訊28at.com

圖片圖片6JB28資訊網——每日最新資訊28at.com

# 堆棧里面放了什么?

線程堆棧還包含正在執行的每個方法的所有局部變量(調用堆棧上的所有方法)。線程只能訪問它自己的線程堆棧。由線程創建的局部變量對于創建它的線程以外的所有其他線程是不可見的。即使兩個線程正在執行完全相同的代碼,兩個線程仍將在每個自己的線程堆棧中創建該代碼的局部變量。因此,每個線程都有自己的每個局部變量的版本。6JB28資訊網——每日最新資訊28at.com

基本類型的所有局部變量(boolean,byte,short,char,int,long,float,double)完全存儲在線程堆棧中,因此對其他線程不可見。一個線程可以將一個基本類型變量的副本傳遞給另一個線程,但它不能共享原始局部變量本身。6JB28資訊網——每日最新資訊28at.com

堆包含了在Java應用程序中創建的所有對象,無論創建該對象的線程是什么。這包括基本類型的包裝類(例如Byte,Integer,Long等)。無論是創建對象并將其分配給局部變量,還是創建為另一個對象的成員變量,該對象仍然存儲在堆上。6JB28資訊網——每日最新資訊28at.com

圖片圖片6JB28資訊網——每日最新資訊28at.com

局部變量可以是基本類型,在這種情況下,它完全保留在線程堆棧上。6JB28資訊網——每日最新資訊28at.com

局部變量也可以是對象的引用。在這種情況下,引用(局部變量)存儲在線程堆棧中,但是對象本身存儲在堆(Heap)上。6JB28資訊網——每日最新資訊28at.com

對象的成員變量與對象本身一起存儲在堆上。當成員變量是基本類型時,以及它是對象的引用時都是如此。6JB28資訊網——每日最新資訊28at.com

靜態類變量也與類定義一起存儲在堆上。6JB28資訊網——每日最新資訊28at.com

線程棧如何訪問堆上對象?

所有具有對象引用的線程都可以訪問堆上的對象。當一個線程有權訪問一個對象時,它也可以訪問該對象的成員變量。如果兩個線程同時在同一個對象上調用一個方法,它們都可以訪問該對象的成員變量,但每個線程都有自己的局部變量副本。6JB28資訊網——每日最新資訊28at.com

圖片圖片6JB28資訊網——每日最新資訊28at.com

兩個線程有一組局部變量。其中一個局部變量(局部變量2)指向堆上的共享對象(對象3)。兩個線程各自對同一對象具有不同的引用。它們的引用是局部變量,因此存儲在每個線程的線程堆棧中(在每個線程堆棧上)。但是,這兩個不同的引用指向堆上的同一個對象。6JB28資訊網——每日最新資訊28at.com

注意共享對象(對象3)如何將對象2和對象4作為成員變量引用(由對象3到對象2和對象4的箭頭所示)。通過對象3中的這些成員變量引用,兩個線程可以訪問對象2和對象4.6JB28資訊網——每日最新資訊28at.com

該圖還顯示了一個局部變量,該變量指向堆上的兩個不同對象。在這種情況下,引用指向兩個不同的對象(對象1和對象5),而不是同一個對象。理論上,如果兩個線程都引用了兩個對象,則兩個線程都可以訪問對象1和對象5。但是在上圖中,每個線程只引用了兩個對象中的一個。6JB28資訊網——每日最新資訊28at.com

線程棧訪問堆示例

那么,什么樣的Java代碼可以導致上面的內存圖? 好吧,代碼就像下面的代碼一樣簡單:6JB28資訊網——每日最新資訊28at.com

public class MyRunnable implements Runnable() {    public void run() {        methodOne();    }    public void methodOne() {        int localVariable1 = 45;        MySharedObject localVariable2 =            MySharedObject.sharedInstance;        //... do more with local variables.        methodTwo();    }    public void methodTwo() {        Integer localVariable1 = new Integer(99);        //... do more with local variable.    }}public class MySharedObject {    //static variable pointing to instance of MySharedObject    public static final MySharedObject sharedInstance =        new MySharedObject();    //member variables pointing to two objects on the heap    public Integer object2 = new Integer(22);    public Integer object4 = new Integer(44);    public long member1 = 12345;    public long member1 = 67890;}

如果兩個線程正在執行run()方法,則前面顯示的圖表將是結果。run()方法調用methodOne(),methodOne()調用methodTwo()。6JB28資訊網——每日最新資訊28at.com

methodOne()聲明一個局部基本類型變量(類型為int的localVariable1)和一個局部變量,它是一個對象引用(localVariable2)。6JB28資訊網——每日最新資訊28at.com

執行methodOne()的每個線程將在各自的線程堆棧上創建自己的localVariable1和localVariable2副本。localVariable1變量將完全相互分離,只存在于每個線程的線程堆棧中。一個線程無法看到另一個線程對其localVariable1副本所做的更改。6JB28資訊網——每日最新資訊28at.com

執行methodOne()的每個線程也將創建自己的localVariable2副本。但是,localVariable2的兩個不同副本最終都指向堆上的同一個對象。代碼將localVariable2設置為指向靜態變量引用的對象。靜態變量只有一個副本,此副本存儲在堆上。因此,localVariable2的兩個副本最終都指向靜態變量指向的MySharedObject的同一個實例。MySharedObject實例也存儲在堆上。它對應于上圖中的對象3。6JB28資訊網——每日最新資訊28at.com

注意MySharedObject類還包含兩個成員變量。成員變量本身與對象一起存儲在堆上。兩個成員變量指向另外兩個Integer對象。這些Integer對象對應于上圖中的Object 2和Object 4。6JB28資訊網——每日最新資訊28at.com

另請注意methodTwo()如何創建名為localVariable1的局部變量。此局部變量是對Integer對象的對象引用。該方法將localVariable1引用設置為指向新的Integer實例。localVariable1引用將存儲在執行methodTwo()的每個線程的一個副本中。實例化的兩個Integer對象將存儲在堆上,但由于該方法每次執行該方法時都會創建一個新的Integer對象,因此執行此方法的兩個線程將創建單獨的Integer實例。在methodTwo()中創建的Integer對象對應于上圖中的Object 1和Object 5。6JB28資訊網——每日最新資訊28at.com

另請注意類型為long的MySharedObject類中的兩個成員變量,它們是基本類型。由于這些變量是成員變量,因此它們仍與對象一起存儲在堆上。只有局部變量存儲在線程堆棧中。6JB28資訊網——每日最新資訊28at.com

JMM與硬件內存結構關系

硬件內存結構簡介

現代硬件內存架構與內部Java內存模型略有不同。了解硬件內存架構也很重要,以了解Java內存模型如何與其一起工作。本節介紹了常見的硬件內存架構,后面的部分將介紹Java內存模型如何與其配合使用。6JB28資訊網——每日最新資訊28at.com

這是現代計算機硬件架構的簡化圖:6JB28資訊網——每日最新資訊28at.com

圖片圖片6JB28資訊網——每日最新資訊28at.com

現代計算機通常有2個或更多CPU。其中一些CPU也可能有多個內核。關鍵是,在具有2個或更多CPU的現代計算機上,可以同時運行多個線程。每個CPU都能夠在任何給定時間運行一個線程。這意味著如果您的Java應用程序是多線程的,線程真的在可能同時運行.6JB28資訊網——每日最新資訊28at.com

每個CPU基本上都包含一組在CPU內存中的寄存器。CPU可以在這些寄存器上執行的操作比在主存儲器中對變量執行的操作快得多。這是因為CPU可以比訪問主存儲器更快地訪問這些寄存器。6JB28資訊網——每日最新資訊28at.com

每個CPU還可以具有CPU高速緩存存儲器層。事實上,大多數現代CPU都有一些大小的緩存存儲層。CPU可以比主存儲器更快地訪問其高速緩存存儲器,但通常不會像訪問其內部寄存器那樣快。因此,CPU高速緩存存儲器介于內部寄存器和主存儲器的速度之間。某些CPU可能有多個緩存層(級別1和級別2),但要了解Java內存模型如何與內存交互,這一點并不重要。重要的是要知道CPU可以有某種緩存存儲層。6JB28資訊網——每日最新資訊28at.com

計算機還包含主存儲區(RAM)。所有CPU都可以訪問主內存。主存儲區通常比CPU的高速緩存存儲器大得多。同時訪問速度也就較慢.6JB28資訊網——每日最新資訊28at.com

通常,當CPU需要訪問主存儲器時,它會將部分主存儲器讀入其CPU緩存。它甚至可以將部分緩存讀入其內部寄存器,然后對其執行操作。當CPU需要將結果寫回主存儲器時,它會將值從其內部寄存器刷新到高速緩沖存儲器,并在某些時候將值刷新回主存儲器。6JB28資訊網——每日最新資訊28at.com

JMM與硬件內存連接 - 引入

如前所述,Java內存模型和硬件內存架構是不同的。硬件內存架構不區分線程堆棧和堆。在硬件上,線程堆棧和堆都位于主存儲器中。線程堆棧和堆的一部分有時可能存在于CPU高速緩存和內部CPU寄存器中。這在圖中說明:6JB28資訊網——每日最新資訊28at.com

圖片圖片6JB28資訊網——每日最新資訊28at.com

當對象和變量可以存儲在計算機的各種不同存儲區域中時,可能會出現某些問題。兩個主要問題是:6JB28資訊網——每日最新資訊28at.com

  • Visibility of thread updates (writes) to shared variables.
  • Race conditions when reading, checking and writing shared variables. 以下各節將解釋這兩個問題。

JMM與硬件內存連接 - 對象共享后的可見性

如果兩個或多個線程共享一個對象,而沒有正確使用volatile聲明或同步,則一個線程對共享對象的更新可能對其他線程不可見。6JB28資訊網——每日最新資訊28at.com

想象一下,共享對象最初存儲在主存儲器中。然后,在CPU上運行的線程將共享對象讀入其CPU緩存中。它在那里對共享對象進行了更改。只要CPU緩存尚未刷新回主內存,共享對象的更改版本對于在其他CPU上運行的線程是不可見的。這樣,每個線程最終都可能擁有自己的共享對象副本,每個副本都位于不同的CPU緩存中。6JB28資訊網——每日最新資訊28at.com

下圖描繪了該情況。在左CPU上運行的一個線程將共享對象復制到其CPU緩存中,并將其count變量更改為2.對于在右邊的CPU上運行的其他線程,此更改不可見,因為計數更新尚未刷新回主內存中.6JB28資訊網——每日最新資訊28at.com

圖片圖片6JB28資訊網——每日最新資訊28at.com

要解決此問題,您可以使用Java的volatile關鍵字。volatile關鍵字可以確保直接從主內存讀取給定變量,并在更新時始終寫回主內存。6JB28資訊網——每日最新資訊28at.com

JMM與硬件內存連接 - 競態條件

如果兩個或多個線程共享一個對象,并且多個線程更新該共享對象中的變量,則可能會出現競態。6JB28資訊網——每日最新資訊28at.com

想象一下,如果線程A將共享對象的變量計數讀入其CPU緩存中。想象一下,線程B也做同樣的事情,但是進入不同的CPU緩存。現在,線程A將一個添加到count,而線程B執行相同的操作。現在var1已經增加了兩次,每個CPU緩存一次。6JB28資訊網——每日最新資訊28at.com

如果這些增量是按先后順序執行的,則變量計數將增加兩次并將原始值+ 2寫回主存儲器。6JB28資訊網——每日最新資訊28at.com

但是,兩個增量同時執行而沒有適當的同步。無論線程A和B中哪一個將其更新后的計數版本寫回主存儲器,更新的值將僅比原始值高1,盡管有兩個增量。6JB28資訊網——每日最新資訊28at.com

該圖說明了如上所述的競爭條件問題的發生:6JB28資訊網——每日最新資訊28at.com

圖片圖片6JB28資訊網——每日最新資訊28at.com

要解決此問題,您可以使用Java synchronized塊。同步塊保證在任何給定時間只有一個線程可以進入代碼的給定關鍵部分。同步塊還保證在同步塊內訪問的所有變量都將從主存儲器中讀入,當線程退出同步塊時,所有更新的變量將再次刷新回主存儲器,無論變量是不是聲明為volatile6JB28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-58953-0.html一文搞定JMM核心原理

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 程序開發中使用XML還是JSON作為數據傳輸格式好?

下一篇: 請注意,你的 Pulsar 集群可能有刪除數據的風險

標簽:
  • 熱門焦點
  • 6月安卓手機性能榜:vivo/iQOO霸占旗艦排行榜前三

    2023年上半年已經正式過去了,我們也迎來了安兔兔V10版本,在新的驍龍8Gen3和天璣9300發布之前,性能榜的榜單大體會以驍龍8Gen2和天璣9200+為主,至于那顆3.36GHz的驍龍8Gen2領先
  • 掘力計劃第 20 期:Flutter 混合開發的混亂之治

    在掘力計劃系列活動第20場,《Flutter 開發實戰詳解》作者,掘金優秀作者,Github GSY 系列目負責人戀貓的小郭分享了Flutter 混合開發的混亂之治。Flutter 基于自研的 Skia 引擎
  • SpringBoot中使用Cache提升接口性能詳解

    環境:springboot2.3.12.RELEASE + JSR107 + Ehcache + JPASpring 框架從 3.1 開始,對 Spring 應用程序提供了透明式添加緩存的支持。和事務支持一樣,抽象緩存允許一致地使用各
  • 讓我們一起聊聊文件的操作

    文件【1】文件是什么?文件是保存數據的地方,是數據源的一種,比如大家經常使用的word文檔、txt文件、excel文件、jpg文件...都是文件。文件最主要的作用就是保存數據,它既可以保
  • 如何使用JavaScript創建一只圖像放大鏡?

    譯者 | 布加迪審校 | 重樓如果您曾經瀏覽過購物網站,可能遇到過圖像放大功能。它可以讓您放大圖像的特定區域,以便瀏覽。結合這個小小的重要功能可以大大改善您網站的用戶體驗
  • 一文搞定Java NIO,以及各種奇葩流

    大家好,我是哪吒。很多朋友問我,如何才能學好IO流,對各種流的概念,云里霧里的,不求甚解。用到的時候,現百度,功能雖然實現了,但是為什么用這個?不知道。更別說效率問題了~下次再遇到,
  • 中國家電海外掘金正當時|出海專題

    作者|吳南南編輯|胡展嘉運營|陳佳慧出品|零態LT(ID:LingTai_LT)2023年,出海市場戰況空前,中國創業者在海外紛紛摩拳擦掌,以期能夠把中國的商業模式、創業理念、戰略打法輸出海外,他們依
  • iQOO Neo8 Pro搶先上架:首發天璣9200+ 安卓性能之王

    經過了一段時間的密集爆料,昨日iQOO官方如期對外宣布:將于5月23日推出全新的iQOO Neo8系列新品,官方稱這是一款擁有旗艦級性能調校的作品。隨著發布時
  • iQOO Neo8系列今日官宣:首發天璣9200+ 全球安卓最強芯!

    在昨日舉行的的聯發科新一代旗艦芯片天璣9200+的發布會上,iQOO官方也正式宣布,全新的iQOO Neo8系列新品將全球首發搭載這款當前性能最強大的移動平臺
Top 主站蜘蛛池模板: 德令哈市| 澎湖县| 莒南县| 怀远县| 普定县| 富平县| 合水县| 石柱| 台东市| 麟游县| 潮安县| 昔阳县| 香格里拉县| 琼中| 鄂尔多斯市| 万盛区| 赤峰市| 中山市| 扎囊县| 揭西县| 夏津县| 方城县| 京山县| 来凤县| 自贡市| 福建省| 新竹县| 会宁县| 宁武县| 正宁县| 顺昌县| 定远县| 阜宁县| 镇赉县| 巴彦淖尔市| 墨竹工卡县| 林芝县| 屯门区| 柘城县| 思南县| 顺义区|