GitHub 4.1k Star 的Java工程師成神之路 ,不來了解一下嗎?git
GitHub 4.1k Star 的Java工程師成神之路 ,真的不來了解一下嗎?程序員
GitHub 4.1k Star 的Java工程師成神之路 ,真的肯定不來了解一下嗎?github
最近,面試過不少Java中高級開發,問過不少次關於Java內存模型的知識,問完以後,不少人上來就開始回答:面試
Java內存模型由幾部分組成,堆、本地方法棧、虛擬機棧、方法區...算法
每一次我不想打斷他們的話,雖然我知道這又是一個誤會了個人問題的朋友。編程
其實,我想問的Java內存模型,是和併發編程有關的。而候選人給我回答的那叫JVM內存結構,徹底是兩回事。緩存
不少時候,在我沒有打斷他們的狀況下,一部分人慢慢的講到了GC相關的知識。這種狀況下,我只能硬着頭皮繼續問一些和JVM有關的知識。多線程
可是,個人本意實際上是想看一下他對Java併發有多少了解啊。併發
常常,我都在繼續追問了一些他們回答的"Java內存模型"相關的知識後,友善的提醒一句,其實我想問的Java內存模型並非他回答的這個...性能
有的時候,我會進一步提醒一句:是和併發編程有關的,是和主內存以及線程工做內存有關的。。。
那麼,本文就來簡單說一說,關於Java內存模型,到底應該如何回答這個面試題。
首先,咱們先來分析一下問什麼不少人,甚至是大多數人會答非所問呢?
我以爲主要有幾個緣由:
一、Java內存模型,這個詞聽着太像是關於內存分佈的知識了。聽上去和併發編程沒有半毛錢關係。
二、網上不少資料都是錯的。不信你去網上搜索一下"Java內存模型",你會發現,不少人打着內存模型的標題,介紹了JVM內存結構的知識。
這裏提一句,我嘗試着Google搜索了一下搜索"Java內存模型",首頁展現結果以下:
首頁排名靠前的5篇文章中,有1篇是錯的,介紹了JVM內存結構。
PS:值得慶幸的的是,首頁前5篇文章中,有兩篇是我寫的,至少個人這兩篇我敢肯定是不具有任何誤導性的!!
三、還存在一種狀況,雖然很少見,可是也有。那就是不少面試官本身也覺得內存模型就是要介紹堆、棧、方法區這些知識。就致使有時候面試者不知道本身到底應該如何回答。
那麼,到底什麼是Java內存模型?關於這道面試題應該如何回答呢?
我曾經在《再有人問你Java內存模型是什麼,就把這篇文章發給他》中詳細的介紹過Java內存模型的前因後果,這裏再從新回顧一下。
Java內存模型是根據英文Java Memory Model(JMM)翻譯過來的。其實JMM並不像JVM內存結構同樣是真實存在的。他只是一個抽象的概念。
Java內存模型的相關知識在 JSR-133: Java Memory Model and Thread Specification 中描述的。JMM是和多線程相關的,他描述了一組規則或規範,這個規範定義了一個線程對共享變量的寫入時對另外一個線程是可見的。
Java內存模型(Java Memory Model ,JMM)就是一種符合內存模型規範的,屏蔽了各類硬件和操做系統的訪問差別的,保證了Java程序在各類平臺下對內存的訪問都能獲得一致效果的機制及規範。目的是解決因爲多線程經過共享內存進行通訊時,存在的原子性、可見性(緩存一致性)以及有序性問題。
那麼,咱們這裏就先來講說什麼是所謂的內存模型規範、這裏提到的原子性、可見性以及有序性又是什麼東西?
原子性
線程是CPU調度的基本單位。CPU有時間片的概念,會根據不一樣的調度算法進行線程調度。因此在多線程場景下,就會發生原子性問題。由於線程在執行一個讀改寫操做時,在執行完讀改以後,時間片耗完,就會被要求放棄CPU,並等待從新調度。這種狀況下,讀改寫就不是一個原子操做。即存在原子性問題。
緩存一致性
在多核CPU,多線程的場景中,每一個核都至少有一個L1 緩存。多個線程訪問進程中的某個共享內存,且這多個線程分別在不一樣的核心上執行,則每一個核心都會在各自的caehe中保留一份共享內存的緩衝。因爲多核是能夠並行的,可能會出現多個線程同時寫各自的緩存的狀況,而各自的cache之間的數據就有可能不一樣。
在CPU和主存之間增長緩存,在多線程場景下就可能存在緩存一致性問題,也就是說,在多核CPU中,每一個核的本身的緩存中,關於同一個數據的緩存內容可能不一致。
有序性
除了引入了時間片之外,因爲處理器優化和指令重排等,CPU還可能對輸入代碼進行亂序執行,好比load->add->save 有可能被優化成load->save->add 。這就是有序性問題。
多CPU多級緩存致使的一致性問題、CPU時間片機制致使的原子性問題、以及處理器優化和指令重排致使的有序性問題等,都硬件的不斷升級致使的。那麼,有沒有什麼機制能夠很好的解決上面的這些問題呢?
最簡單直接的作法就是廢除處理器和處理器的優化技術、廢除CPU緩存,讓CPU直接和主存交互。可是,這麼作雖然能夠保證多線程下的併發問題。可是,這就有點因噎廢食了。
因此,爲了保證併發編程中能夠知足原子性、可見性及有序性。有一個重要的概念,那就是——內存模型。
爲了保證共享內存的正確性(可見性、有序性、原子性),內存模型定義了共享內存系統中多線程程序讀寫操做行爲的規範。經過這些規則來規範對內存的讀寫操做,從而保證指令執行的正確性。它與處理器有關、與緩存有關、與併發有關、與編譯器也有關。他解決了CPU多級緩存、處理器優化、指令重排等致使的內存訪問問題,保證了併發場景下的一致性、原子性和有序性。
針對上面的這些問題,不一樣的操做系統都有不一樣的解決方案,而Java語言爲了屏蔽掉底層的差別,定義了一套屬於Java語言的內存模型規範,即Java內存模型。
Java內存模型規定了全部的變量都存儲在主內存中,每條線程還有本身的工做內存,線程的工做內存中保存了該線程中是用到的變量的主內存副本拷貝,線程對變量的全部操做都必須在工做內存中進行,而不能直接讀寫主內存。不一樣的線程之間也沒法直接訪問對方工做內存中的變量,線程間變量的傳遞均須要本身的工做內存和主存之間進行數據同步進行。
而JMM就做用於工做內存和主存之間數據同步過程。他規定了如何作數據同步以及何時作數據同步。
瞭解Java多線程的朋友都知道,在Java中提供了一系列和併發處理相關的關鍵字,好比volatile、synchronized、final、concurren包等。其實這些就是Java內存模型封裝了底層的實現後提供給程序員使用的一些關鍵字。
在開發多線程的代碼的時候,咱們能夠直接使用synchronized等關鍵字來控制併發,歷來就不須要關心底層的編譯器優化、緩存一致性等問題。因此,Java內存模型,除了定義了一套規範,還提供了一系列原語,封裝了底層實現後,供開發者直接使用。
本文並不許備把全部的關鍵字逐一介紹其用法,由於關於各個關鍵字的用法,網上有不少資料。讀者能夠自行學習。本文還有一個重點要介紹的就是,咱們前面提到,併發編程要解決原子性、有序性和一致性的問題,咱們就再來看下,在Java中,分別使用什麼方式來保證。
原子性
在Java中,爲了保證原子性,提供了兩個高級的字節碼指令monitorenter和monitorexit。在synchronized的實現原理文章中,介紹過,這兩個字節碼,在Java中對應的關鍵字就是synchronized。
所以,在Java中可使用synchronized來保證方法和代碼塊內的操做是原子性的。
可見性
Java內存模型是經過在變量修改後將新值同步回主內存,在變量讀取前從主內存刷新變量值的這種依賴主內存做爲傳遞媒介的方式來實現的。
Java中的volatile關鍵字提供了一個功能,那就是被其修飾的變量在被修改後能夠當即同步到主內存,被其修飾的變量在每次是用以前都從主內存刷新。所以,可使用volatile來保證多線程操做時變量的可見性。
除了volatile,Java中的synchronized和final兩個關鍵字也能夠實現可見性。只不過實現方式不一樣,這裏再也不展開了。
有序性
在Java中,可使用synchronized和volatile來保證多線程之間操做的有序性。實現方式有所區別:
volatile關鍵字會禁止指令重排。synchronized關鍵字保證同一時刻只容許一條線程操做。
好了,這裏簡單的介紹完了Java併發編程中解決原子性、可見性以及有序性可使用的關鍵字。讀者可能發現了,好像synchronized關鍵字是萬能的,他能夠同時知足以上三種特性,這其實也是不少人濫用synchronized的緣由。
可是synchronized是比較影響性能的,雖然編譯器提供了不少鎖優化技術,可是也不建議過分使用。
前面我介紹完了一些和Java內存模型有關的基礎知識,只是基礎,並非所有,由於隨便一個知識點仍是均可以展開的,如volatile是如何實現可見性的?synchronized是如何實現有序性的?
可是,當面試官問你:能簡單介紹下你理解的內存模型嗎?
首先,先和麪試官確認一下:您說的內存模型指的是JMM,也就是和併發編程有關的那一個吧?
在獲得確定答覆後,再開始介紹(若是不是,那可能就要回答堆、棧、方法區哪些了....囧...):
Java內存模型,實際上是保證了Java程序在各類平臺下對內存的訪問都可以獲得一致效果的機制及規範。目的是解決因爲多線程經過共享內存進行通訊時,存在的原子性、可見性(緩存一致性)以及有序性問題。
除此以外,Java內存模型還提供了一系列原語,封裝了底層實現後,供開發者直接使用。如咱們經常使用的一些關鍵字:synchronized、volatile以及併發包等。
回答到這裏就能夠了,而後面試官可能會繼續追問,而後根據他的追問再繼續往下回答便可。
因此,當有人再問你Java內存模型的時候,不要一張嘴就直接回答堆棧、方法區甚至GC了,那樣顯得很不專業!