您好,登錄后才能下訂單哦!
本篇內容介紹了“java內存模型和java內存結構有什么區別”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
記得是在好幾年前研究Android的時候,看的java內存模型,時常和java內存結構分不清,因此,這一小節是針對小白或者是對其概念還不理解的人。
我們都知道,我們的java代碼其實是不能直接運行的,他要經過一系列的步驟。看下圖:
我們的java文件,首先要經過編程成為class文件,然后通過類裝載器加載到jvm中去執行。這個jvm(紅色虛線框起來的這部分)就是java運行時數據區,意思就是java代碼在運行的時候,這些數據要存放在不同的內存空間里面。jvm就是指代這個的。當然了上面的運行時數據區jvm是jdk1.7版本的。也就是說不同的jdk版本,這個jvm長得是不一樣的。我們可以把java7的內存結構拿出來:
我們可以看到一共劃分了5個部分,其中java堆區和方法區還是所有線程共享的一片區域。為什么要所有線程共享呢?因為假設一個數據,每個線程都保留一份,那其中有一個線程調皮,把這個數據更改了。其他的線程發現自己的數據沒有變,這就出現了問題了。于是設計成了所有線程共享,java內存模型出來了。
java內存模型也叫做JMM,但是這個模型可不是像java內存結構一樣,是真實存在的。java內存模型是一個抽象出來的概念。意思是把一部分內存區域設計成所有線程共享的,一個線程對數據更改,其他線程就能立刻知道。這種設計的方法叫做內存模型。我們可以提前看一下:java內存模型長什么樣。
這就是java內存模型,也就是多個線程共享同一份數據。現在不知道你理解java內存模型和java內存結構的區別了沒有,我們可以這樣來總結一下:
(1)java內存結構是解決java中的數據如何存放的問題。
(2)java內存模型是解決java中多個線程共享數據的問題。
OK,到了這基本上就算是把兩者的區別介紹完了。下面就來看看為什么要有內存模型吧
深入理解java虛擬機是從硬件的發展來分析的。因此,我也將從這個角度來分析。
在計算機發展的第一個階段,程序是在CPU中運行,數據在主存中保存。隨著技術的發展,CPU的速度越來越多高,但是主存的速度卻沒有提高太多。就好比是下面這種情況:
為了解決上面的問題,于是乎出現了緩存,里面存放了一些CPU經常使用的主存數據,緩存的速度和CPU差不多,當CPU查找數據的時候,首先從緩存中查找,沒有的話再從主存中查找。寫數據的時候,先寫緩存的數據,然后再更新到主存中。這樣一種機制使得速度提高很多。
技術繼續發展,在上面緩存的基礎上出現了一級二級三級緩存,查找也是逐層的,第一級緩存沒有就到第二層,就這樣以此類推。這時候CPU也得到了快讀發展,由之前的一個核變成了多核CPU(一個CPU變成了多部分)。
這時候呢,之前只能同時跑一個線程,現在就能跑很多個線程了。而且從上面我們可以看到,每一個核都有相應的緩存區,但是主內存還是哪一個。既然能同時跑多個線程,那速度肯定杠杠滴就上去了吧,不跑不知道,一跑嚇一跳,立馬出現了很多個問題。
問題一:緩存一致性問題
也就是說,每一個核都有自己的緩存區,但是這些緩存區保存的數據卻不一樣。一張圖就明白了:
問題二:處理器優化和指令重排
這問題的意思是,既然CPU有這么多內核,肯定是想讓資源得到充分利用,于是把我們寫的程序拆分,對一些代碼進行亂序處理,這就是處理器優化。而且,java虛擬機一看CPU的這個操作真的強,于是就模仿了一下,創建了即時編譯器(JIT),這個編譯器也會做指令重排的操作。很明顯,我們的代碼順序被打亂,指令被重排,就可能不會按照我們的意愿去執行了。
上面出現的這些問題,好像都是從硬件的角度來分析的,《深入理解java虛擬機》一書于是引出了軟件的問題,也就是說,上面的這些問題如果轉化到軟件層次會帶來什么問題呢?
問題三:軟件問題
(1)原子性問題
首先緩存一致性問題在程序中會帶來原子性問題,原子性問題是什么意思呢?你首先就要先理解原子。在生物里面原子叫做不可再分的物質。在軟件里面,原子叫做不可再分的程序操作。而原子性問題肯定就是打破了這個規則,也就是說在這個操作中又進行了拆分。
在緩存一致性問題中,兩個CPU內核中a的數據不一致,也就是說兩個CPU內核讀取主存a的值是不一樣的。那么對于a的更改這個操作肯定就不是原子性,在A更改的過程中,兩個CPU內核同時進行了更改。
(2)可見性問題
上面在介紹原子性問題的時候說了,兩個線程(CPU內核)訪問同一個變量時,線程2修改了這個變量的值,但是線程1卻沒有看到其修改,所以讀的仍然是舊數據。
(3)有序性
也就是程序沒有按照指定的順序去執行。
可見性問題和有序性問題就是由于處理器優化和執行重排造成的。
針對于這么多問題,于是java虛擬機提出了一個java內存模型。有效地解決了上面出現的這三個問題:
為了和開頭進行對照,我們再給出以此他的內存模型圖:
從這張圖我們分析一下java內存模型是如何解決上面的三個問題的。
規則一:所有的數據都在主內存中。
規則二:每個線程都保留一份共享變量的副本。線程對變量的所有操作都必須在這個副本內存中進行,而不能直接讀寫主內存。
規則三:不同的線程之間也無法直接訪問對方工作內存中的變量,線程間變量的傳遞均需要自己的工作內存和主存之間進行數據同步進行。
看文字有點亂,我們舉個例子,這個例子我覺得不太恰當,但是結合著上面的概念,相信你會明白的。在古代的時候經常發生旱災,朝廷去賑災,于是拿了一個大鐵鍋煮粥,這個大鐵鍋就是主內存,里面的粥就是數據。而每個難民就代表了不同的線程。
(1)難民都有一個碗,盛放一碗粥。就好比是每個線程都有一個本地內存,有一個主內存的副本。
(2)難民喝粥就只能在自己碗里,不能直接爬到鍋中喝粥,就好比線程只能在自己的本地內存中操作數據,不能直接到主內存讀寫數據。
(3)其中一個難民想要把粥分給伙伴,怎么辦呢?這個難民要先把粥倒進鍋里,同伴再去鍋里盛。就好比線程不能直接訪問對方的內存,他們之間的數據傳遞都是通過主內存。
這個例子希望你能明白。現在這個模型算是出來了,還有一個問題沒有解決,那就是java提供了什么東西來實現的這三個規則呢?
由于提供的機制太多,我們可以簡單的例舉幾個,比如說synchronized關鍵字保證了原子性,volatile關鍵字保證了可見性。synchronized關鍵字和volatile關鍵字保證了有序性。當然還有很多的Lock機制,并發包里面等等都是為了解決這三個問題提出來的。
其中有一條很重要的規則叫做happens-before原則,這條原則是為了解決可見性提出來的。什么意思呢?
如果一個操作的執行結果需要對另一個操作可見,那么這兩個操作之間必須要存在happens-before關系。舉個例子:
(1)程序順序規則:一個線程中的每個操作發生在后一個操作之前,這就是happens-before。
(2)鎖規則:對于鎖機制,一定要先加鎖,才能解鎖,這也是happens-before。
(3)volatile域規則:對一個volatile域的寫操作一定要發生在讀操作前面。
“java內存模型和java內存結構有什么區別”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。