一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java內存模型的深入講解

Java內存模型的深入講解

2021-09-27 10:08surzia Java教程

這篇文章主要給大家介紹了關于Java內存模型的相關資料,我們常說的JVM內存模式指的是JVM的內存分區,而Java內存模式是一種虛擬機規范,需要的朋友可以參考下

Java內存模型展示了Java虛擬機是如何與計算機內存交互的,解決多線程讀寫共享內存時資源訪問的問題。

 

內存模型

Java虛擬機中的內存模型將線程棧與堆劃分開,下圖描述了Java內存模型的邏輯圖。

Java內存模型的深入講解

每個線程都要自己的線程棧,棧中存儲著線程執行到當前位置所調用的方法信息,線程執行代碼時,線程棧會不斷執行入棧和出棧操作。

線程棧中會存儲所有被調用的方法中定義的變量,并且自己訪問自己棧中的變量,別的線程不可見。即使兩個線程執行相同的代碼,也會在線程自己的棧中重復創建變量。一個線程可能會傳遞變量副本給另一個線程,但不能共享變量本身。

在棧中變量存儲形式也有所不同。屬于基本變量類型(int,byte,long,boolean,char,double,float,short)的變量,會直接將變量值存儲在棧中,而其余類型的變量的值被存儲在堆中,線程棧中只保留指向堆中變量地址的指針。
堆中則存儲Java程序中創建的所有對象,不管是什么線程創建的。創建對象并將其分配給局部變量,或者將其創建為另一個對象的成員變量都沒有影響,該對象仍存儲在堆中。

Java內存模型的深入講解

值得注意的是,Java中的靜態類變量也會隨著類初始化而存儲在堆中。

有指向對象指針的所有線程都可以訪問堆上的對象。當線程可以訪問對象時,它也可以訪問該對象的成員變量。如果兩個線程同時在同一個對象上調用一個方法,則它們都將有權訪問該對象的成員變量,但是每個線程將擁有自己的局部變量副本。

Java內存模型的深入講解

兩個線程有一組局部變量,指向堆上的共享對象。這兩個線程分別具有對同一對象的不同指針。它們的指針也是局部變量,因此存儲在每個線程的線程棧中(在每個線程上)。但是,兩個不同的指針指向堆上的同一對象。
下面的代碼塊就是上圖的一個實際例子。

public class MyRunnable implements Runnable() {

    public void run() {
        methodOne();
    }

    public void methodOne() {
        int localVariable1 = 45;
        MySharedObject localVariable2 = MySharedObject.sharedInstance;
        //...
        methodTwo();
    }

    public void methodTwo() {
        Integer localVariable1 = new Integer(99);
        //...
    }
}
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 member2 = 67890;
}

 

硬件架構

現代硬件的內存架構與Java內存模型還是有些不同的,了解硬件架構對理解Java內存模型也有幫助。簡單的硬件架構圖如下:

Java內存模型的深入講解

現代計算機一般是多核CPU,一般不止一個CPU,因此多個線程是可能在物理意義上并發運行的。這意味著,如果Java應用程序是多線程的,則每個CPU可能在Java應用程序中同時(并發)運行一個線程。

每個CPU包含一組寄存器,這些寄存器本質上是CPU內存儲器。CPU在這些寄存器上執行操作的速度比對主存儲器中的變量執行操作的速度快得多,這是因為CPU可以比訪問主存儲器更快地訪問這些寄存器。

每個CPU可能還具有一個CPU高速緩存。實際上,大多數現代CPU都有一定大小的高速緩存。CPU可以比其主存儲器更快地訪問其高速緩存,但是通常不如其訪問其內部寄存器的速度快。因此,CPU高速緩存存儲器位于內部寄存器和主存儲器之間的速度之間。某些CPU可能具有多個高速緩存層(L1和L2 Cache)。了解Java內存模型如何與內存交互并不是很重要,重要的是要知道CPU可以具有某種高速緩存層。

計算機還包含一個主存儲區(RAM)。所有CPU都可以訪問主存儲器。主存儲區通常比CPU的高速緩存大得多。

通常,當CPU需要訪問主內存時,它將部分主內存讀入其CPU緩存中。它甚至可以將緩存的一部分讀入其內部寄存器,然后對其執行操作。當CPU需要將結果寫回主存儲器時,它將把值從其內部寄存器刷新到高速緩存,然后在某個時候將值刷新回主存儲器。

當CPU需要將其他內容存儲在高速緩存中時,通常會將高速緩存中存儲的值刷新回主存儲器。CPU高速緩存可以一次將數據寫入其部分內存,并一次刷新其部分內存。它不必每次更新都讀取/寫入完整的緩存。通常,緩存在稱為“緩存行”的較小存儲塊中更新,可以將一個或多個高速緩存行讀入高速緩存存儲器,并且可以將一個或多個高速緩存行再次刷新回主存儲器。

 

Java內存模型與硬件關聯

如前所述,Java內存模型和硬件內存體系結構是不同的,硬件內存體系結構不能區分線程堆棧和堆。在硬件上,線程堆棧和堆都位于主內存中。線程堆棧和堆的某些部分有時可能會出現在CPU緩存和內部CPU寄存器中。下圖對此進行了說明:

Java內存模型的深入講解

當對象和變量可以存儲在計算機的各種不同存儲區域中時,可能會出現某些問題。 兩個主要問題是:

  • 線程更新(寫入)到共享變量的可見性。
  • 讀取,檢查和寫入共享變量時的競爭條件。

 

對象的可見性

如果兩個或多個線程共享一個對象,而沒有正確使用volatile關鍵字,則一個線程對共享對象進行的更新可能對其他線程不可見。

每個線程都可以擁有自己的共享庫副本,每個副本位于不同的CPU緩存中。想象一下,共享對象最初存儲在主存儲器中。然后,在CPU上運行的一個線程將共享對象讀入其CPU緩存并進行修改。只要未將CPU緩存刷新回主存儲器,在其他CPU上運行的線程就看不到共享對象的更改版本。

下圖說明了這種情況,在左CPU上運行的一個線程將共享對象復制到其CPU緩存中,并將其count變量更改為2。在右CPU上運行的其他線程看不到此更改,因為尚未將count更新寫回主內存。

Java內存模型的深入講解

當然這個問題可以使用volatile關鍵字來解決。

 

競爭條件

如果兩個或多個線程共享一個對象,并且一個以上的線程更新該共享對象中的變量,則可能會發生競爭條件。

假如線程A將共享對象的變量count讀入其CPU緩存中,而線程B執行同樣操作,但是它位于不同的CPU緩存中。現在,線程A加一個要計數,線程B也執行相同的操作?,F在count已增加兩次,在每個CPU高速緩存中增加一次。

如果這些增加是順序執行的,則變量計數將增加兩次,并將原始值+2寫回到主存儲器中。

但是,這兩個增量是在沒有同步的情況下并發執行的。不管線程A和B中哪個線程將其更新后的版本寫回主內存,盡管有兩個增量,但更新后的值僅比原始值高1。

該圖說明了如上所述的競爭條件問題的發生:

Java內存模型的深入講解

這個問題可以使用synchronized關鍵字來解決。

 

總結

到此這篇關于Java內存模型的文章就介紹到這了,更多相關Java內存模型內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://juejin.cn/post/6965826554425245726

延伸 · 閱讀

精彩推薦
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7472021-02-04
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
主站蜘蛛池模板: 午夜欧美精品久久久久久久 | 苍井空av| 国产精品主播在线 | www.男人的天堂 | 我要看逼 | 国产视频91在线 | 免费的毛片视频 | 精品一区视频 | 亚洲精品乱码久久久久久蜜桃欧美 | 欧美精品一线二线大片 | 日本三级成人中文字幕乱码 | 国产午夜精品不卡视频 | 青柠网在线观看视频 | 男人狂躁女人下面狂叫图片 | 免费在线观看视频 | 亚瑟天堂久久一区二区影院 | 国内小情侣一二三区在线视频 | 亚洲 欧美 国产 综合久久 | 午夜dj影院在线观看完整版 | 12一14性水蜜桃 | free性丰满hd性欧美厨房 | 国内精品91东航翘臀女神在线 | 色综合欧美色综合七久久 | 日本道色综合久久影院 | 日本男男漫画 | 欧美一区二区视频 | chinese调教踩踏视频 | yellow高清视频日本动漫 | 果冻传媒第一二三专区 | 欧美成人一区二区三区 | 色色色色色色网 | 欧美同性video | 国产五月天在线 | blacked黑人hd2021| 色老板成人永久免费视频 | 精品免费tv久久久久久久 | 婷婷综合亚洲 | 好涨好爽好大视频免费 | 免费一区二区视频 | 国产高清一区二区 | 国产精品调教 |