溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java內(nèi)存模型該怎么理解

發(fā)布時間:2021-12-01 09:51:41 來源:億速云 閱讀:129 作者:iii 欄目:編程語言

這篇文章主要講解了“Java內(nèi)存模型該怎么理解”,文中的講解內(nèi)容簡單清晰,易于學(xué)習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習“Java內(nèi)存模型該怎么理解”吧!

1.為什么會誤解

首先,我們先來分析一下問什么很多人,甚至是大多數(shù)人會答非所問呢?

我覺得主要有幾個原因:

1、Java內(nèi)存模型,這個詞聽著太像是關(guān)于內(nèi)存分布的知識了。聽上去和并發(fā)編程沒有半毛錢關(guān)系。

2、網(wǎng)上很多資料都是錯的。不信你去網(wǎng)上搜索一下"Java內(nèi)存模型",你會發(fā)現(xiàn),很多人打著內(nèi)存模型的標題,介紹了JVM內(nèi)存結(jié)構(gòu)的知識。

3、還存在一種情況,雖然不多見,但是也有。那就是很多面試官自己也以為內(nèi)存模型就是要介紹堆、棧、方法區(qū)這些知識。就導(dǎo)致有時候面試者不知道自己到底應(yīng)該如何回答。

那么,到底什么是Java內(nèi)存模型?關(guān)于這道面試題應(yīng)該如何回答呢?

2.什么是內(nèi)存模型

我曾經(jīng)在《再有人問你Java內(nèi)存模型是什么,就把這篇文章發(fā)給他》中詳細的介紹過Java內(nèi)存模型的來龍去脈,這里再重新回顧一下。

Java內(nèi)存模型是根據(jù)英文Java Memory  Model(JMM)翻譯過來的。其實JMM并不像JVM內(nèi)存結(jié)構(gòu)一樣是真實存在的。他只是一個抽象的概念。

Java內(nèi)存模型的相關(guān)知識在 JSR-133: Java Memory Model and Thread Specification  中描述的。JMM是和多線程相關(guān)的,他描述了一組規(guī)則或規(guī)范,這個規(guī)范定義了一個線程對共享變量的寫入時對另一個線程是可見的。

Java內(nèi)存模型(Java Memory Model  ,JMM)就是一種符合內(nèi)存模型規(guī)范的,屏蔽了各種硬件和操作系統(tǒng)的訪問差異的,保證了Java程序在各種平臺下對內(nèi)存的訪問都能得到一致效果的機制及規(guī)范。目的是解決由于多線程通過共享內(nèi)存進行通信時,存在的原子性、可見性(緩存一致性)以及有序性問題。

那么,我們這里就先來說說什么是所謂的內(nèi)存模型規(guī)范、這里提到的原子性、可見性以及有序性又是什么東西?

原子性

線程是CPU調(diào)度的基本單位。CPU有時間片的概念,會根據(jù)不同的調(diào)度算法進行線程調(diào)度。所以在多線程場景下,就會發(fā)生原子性問題。因為線程在執(zhí)行一個讀改寫操作時,在執(zhí)行完讀改之后,時間片耗完,就會被要求放棄CPU,并等待重新調(diào)度。這種情況下,讀改寫就不是一個原子操作。即存在原子性問題。

緩存一致性

在多核CPU,多線程的場景中,每個核都至少有一個L1  緩存。多個線程訪問進程中的某個共享內(nèi)存,且這多個線程分別在不同的核心上執(zhí)行,則每個核心都會在各自的caehe中保留一份共享內(nèi)存的緩沖。由于多核是可以并行的,可能會出現(xiàn)多個線程同時寫各自的緩存的情況,而各自的cache之間的數(shù)據(jù)就有可能不同。

在CPU和主存之間增加緩存,在多線程場景下就可能存在緩存一致性問題,也就是說,在多核CPU中,每個核的自己的緩存中,關(guān)于同一個數(shù)據(jù)的緩存內(nèi)容可能不一致。

有序性

除了引入了時間片以外,由于處理器優(yōu)化和指令重排等,CPU還可能對輸入代碼進行亂序執(zhí)行,比如load->add->save  有可能被優(yōu)化成load->save->add 。這就是有序性問題。

多CPU多級緩存導(dǎo)致的一致性問題、CPU時間片機制導(dǎo)致的原子性問題、以及處理器優(yōu)化和指令重排導(dǎo)致的有序性問題等,都硬件的不斷升級導(dǎo)致的。那么,有沒有什么機制可以很好的解決上面的這些問題呢?

最簡單直接的做法就是廢除處理器和處理器的優(yōu)化技術(shù)、廢除CPU緩存,讓CPU直接和主存交互。但是,這么做雖然可以保證多線程下的并發(fā)問題。但是,這就有點因噎廢食了。

所以,為了保證并發(fā)編程中可以滿足原子性、可見性及有序性。有一個重要的概念,那就是——內(nèi)存模型。

為了保證共享內(nèi)存的正確性(可見性、有序性、原子性),內(nèi)存模型定義了共享內(nèi)存系統(tǒng)中多線程程序讀寫操作行為的規(guī)范。通過這些規(guī)則來規(guī)范對內(nèi)存的讀寫操作,從而保證指令執(zhí)行的正確性。它與處理器有關(guān)、與緩存有關(guān)、與并發(fā)有關(guān)、與編譯器也有關(guān)。他解決了CPU多級緩存、處理器優(yōu)化、指令重排等導(dǎo)致的內(nèi)存訪問問題,保證了并發(fā)場景下的一致性、原子性和有序性。

針對上面的這些問題,不同的操作系統(tǒng)都有不同的解決方案,而Java語言為了屏蔽掉底層的差異,定義了一套屬于Java語言的內(nèi)存模型規(guī)范,即Java內(nèi)存模型。

Java內(nèi)存模型規(guī)定了所有的變量都存儲在主內(nèi)存中,每條線程還有自己的工作內(nèi)存,線程的工作內(nèi)存中保存了該線程中是用到的變量的主內(nèi)存副本拷貝,線程對變量的所有操作都必須在工作內(nèi)存中進行,而不能直接讀寫主內(nèi)存。不同的線程之間也無法直接訪問對方工作內(nèi)存中的變量,線程間變量的傳遞均需要自己的工作內(nèi)存和主存之間進行數(shù)據(jù)同步進行。

而JMM就作用于工作內(nèi)存和主存之間數(shù)據(jù)同步過程。他規(guī)定了如何做數(shù)據(jù)同步以及什么時候做數(shù)據(jù)同步。

3.Java內(nèi)存模型的實現(xiàn)

了解Java多線程的朋友都知道,在Java中提供了一系列和并發(fā)處理相關(guān)的關(guān)鍵字,比如volatile、synchronized、final、concurren包等。其實這些就是Java內(nèi)存模型封裝了底層的實現(xiàn)后提供給程序員使用的一些關(guān)鍵字。

在開發(fā)多線程的代碼的時候,我們可以直接使用synchronized等關(guān)鍵字來控制并發(fā),從來就不需要關(guān)心底層的編譯器優(yōu)化、緩存一致性等問題。所以,Java內(nèi)存模型,除了定義了一套規(guī)范,還提供了一系列原語,封裝了底層實現(xiàn)后,供開發(fā)者直接使用。

本文并不準備把所有的關(guān)鍵字逐一介紹其用法,因為關(guān)于各個關(guān)鍵字的用法,網(wǎng)上有很多資料。讀者可以自行學(xué)習。本文還有一個重點要介紹的就是,我們前面提到,并發(fā)編程要解決原子性、有序性和一致性的問題,我們就再來看下,在Java中,分別使用什么方式來保證。

原子性

在Java中,為了保證原子性,提供了兩個高級的字節(jié)碼指令monitorenter和monitorexit。在synchronized的實現(xiàn)原理文章中,介紹過,這兩個字節(jié)碼,在Java中對應(yīng)的關(guān)鍵字就是synchronized。

因此,在Java中可以使用synchronized來保證方法和代碼塊內(nèi)的操作是原子性的。

可見性

Java內(nèi)存模型是通過在變量修改后將新值同步回主內(nèi)存,在變量讀取前從主內(nèi)存刷新變量值的這種依賴主內(nèi)存作為傳遞媒介的方式來實現(xiàn)的。

Java中的volatile關(guān)鍵字提供了一個功能,那就是被其修飾的變量在被修改后可以立即同步到主內(nèi)存,被其修飾的變量在每次是用之前都從主內(nèi)存刷新。因此,可以使用volatile來保證多線程操作時變量的可見性。

除了volatile,Java中的synchronized和final兩個關(guān)鍵字也可以實現(xiàn)可見性。只不過實現(xiàn)方式不同,這里不再展開了。

有序性

在Java中,可以使用synchronized和volatile來保證多線程之間操作的有序性。實現(xiàn)方式有所區(qū)別:

volatile關(guān)鍵字會禁止指令重排。synchronized關(guān)鍵字保證同一時刻只允許一條線程操作。

好了,這里簡單的介紹完了Java并發(fā)編程中解決原子性、可見性以及有序性可以使用的關(guān)鍵字。讀者可能發(fā)現(xiàn)了,好像synchronized關(guān)鍵字是***的,他可以同時滿足以上三種特性,這其實也是很多人濫用synchronized的原因。

但是synchronized是比較影響性能的,雖然編譯器提供了很多鎖優(yōu)化技術(shù),但是也不建議過度使用。

4.面試如何回答

前面我介紹完了一些和Java內(nèi)存模型有關(guān)的基礎(chǔ)知識,只是基礎(chǔ),并不是全部,因為隨便一個知識點還是都可以展開的,如volatile是如何實現(xiàn)可見性的?synchronized是如何實現(xiàn)有序性的?

但是,當面試官問你:能簡單介紹下你理解的內(nèi)存模型嗎?

首先,先和面試官確認一下:您說的內(nèi)存模型指的是JMM,也就是和并發(fā)編程有關(guān)的那一個吧?

在得到肯定答復(fù)后,再開始介紹(如果不是,那可能就要回答堆、棧、方法區(qū)哪些了….囧… 這里也可以根據(jù)上下文自行理解面試官想要問的問題具體是什么):

Java內(nèi)存模型,其實是保證了Java程序在各種平臺下對內(nèi)存的訪問都能夠得到一致效果的機制及規(guī)范。目的是解決由于多線程通過共享內(nèi)存進行通信時,存在的原子性、可見性(緩存一致性)以及有序性問題。

除此之外,Java內(nèi)存模型還提供了一系列原語,封裝了底層實現(xiàn)后,供開發(fā)者直接使用。如我們常用的一些關(guān)鍵字:synchronized、volatile以及并發(fā)包等。

回答到這里就可以了,然后面試官可能會繼續(xù)追問,然后根據(jù)他的追問再繼續(xù)往下回答即可。

所以,當有人再問你Java內(nèi)存模型的時候,不要一張嘴就直接回答堆棧、方法區(qū)甚至GC了,先思考下面試官的問題。

感謝各位的閱讀,以上就是“Java內(nèi)存模型該怎么理解”的內(nèi)容了,經(jīng)過本文的學(xué)習后,相信大家對Java內(nèi)存模型該怎么理解這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI