面試官:什麼是Java內存模型?你知道嗎?

面試官:什麼是Java內存模型?



面試官:什麼是Java內存模型?你知道嗎?




前言

不要爲了讀文章而讀文章,一定要帶着問題來讀文章,勤思考。

要想深入瞭解Java併發編程,就要先理解好Java內存模型,而要理解Java內存模型又不得不從硬件、計算機內存模型說起,本文從計算機內存模型產生的原因、解決的問題談起,然後再對Java模型進行介紹,最後對計算機內存模型和Java內存模型進行總結,希望大家看完本文之後有所收穫!

CPU執行過程

大家都知道,計算機在執行程序時,每條指令都是在CPU中執行的,而執行的時候,又免不了要和數據打交道,而計算機上面的臨時數據,是儲存在主存中的。

計算機內存包括高速緩存和主存。

我們知道CPU執行指令的速度比從主存讀取數據和向主存寫入數據快很多,所以爲了高效利用CPU,CPU增加了高速緩存(cache)來匹配CPU的執行速度,最終程序的執行過程如下

  1. 首先會將數據從主存中複製一份到CPU的高速緩存中

  2. 當CPU執行計算的時候就可以直接從高速緩存中讀取數據和寫入數據

  3. 當運算結束後,再將高速緩存的數據更新到主存中


緩存一致性問題

上面的執行過程在單線程情況下並沒有問題,但是在多線程情況下就會出現問題,因爲CPU如果含有多個核心,則每個核心都有自己獨佔高速緩存,如果出現多個線程同時執行同一個操作,那麼結果是無法預知。例如2個線程同時執行i++,假設i的初始值是0,那麼我們希望2個線程執行完成之後i的值變爲2,但是事實會是這樣嗎?

面試官:什麼是Java內存模型?你知道嗎?



可能出現的情況有:

  1. 線程1先將i=0從主存中讀取到線程1的高速緩存中,然後CPU完成運算,再將i=1寫入到主存中,然後線程2開始從主存中讀取i=1到線程2的高速緩存中,然後CPU完成運算,再將i=2寫入到主存中,那麼i=2即爲我們想要的結果。

  2. 線程1將i=0從主存中讀取到線程1的高速緩存中的同時線程2也從主存中讀取i=0到線程2的高速緩存中,然後線程1和線程2完成運算後,也都將i=1寫入到主存中,那麼結果i=1,結果就不是我們想要的了。出現這個情況,我們稱爲緩存不一致問題。


那麼如何解決CPU出現的緩存不一致問題呢?通常使用的解決方法有2種:

  1. 通過給總線加鎖

  2. 使用緩存一致性協議


面試官:什麼是Java內存模型?你知道嗎?



第1種方法雖然也達到了目的,但是在總線被鎖住的期間,其他的CPU也無法訪問主存,效率很低,所以就出現了緩存一致性協議即第2種方法,其中最出名的就是Intel的MESI協議,MESI協議保證每個CPU高速緩存中的變量都是一致的。它的核心思想是,當CPU寫數據時候,如果發現操作的變量是共享變量(即其他CPU上也存在該變量),就會發出信號通知其他CPU將它高速緩存中緩存這個變量的緩存行置爲無效狀態,因此當其他CPU需要讀取這個變量時,發現自己高速緩存中緩存該變量的緩存行爲無效狀態,那麼它就會從主存中重新讀取。

面試官:什麼是Java內存模型?你知道嗎?



處理器重排序問題

在多線程場景下,CPU除了會出現緩存一致性問題,還會出現因爲處理器重排序即處理器(CPU)爲了提高效率可能會對輸入的代碼進行亂序執行,而造成多線程的情況下出現問題。

例如:


//線程1:
context = loadContext(); //語句1
inited = true; //語句2
 
//線程2:
while(!inited ){
 sleep()
}
doSomethingwithconfig(context);

線程1由於處理器重排序,先執行性了語句2,那麼此時線程2會認爲context已經初始化完成,那麼跳出循環,去執行doSomethingwithconfig(context)方法,實際上此時context並未初始化(即線程1的語句1還未執行),而導致程序出錯。

什麼是計算機內存模型

上面提到的緩存一致性問題、處理器重排序問題都是在多線程情況下CPU可能出現的問題,那我們應該怎麼處理這些問題?

可見性即當一個變量修改後,這個變量會馬上更新到主存中,其他線程會收到通知這個變量修改過了,使用這個變量的時候重新去主存獲取


什麼是Java內存模型

從前面的介紹瞭解到計算機內存模型是一種解決多線程場景下的一個主存操作規範,既然是規範,那麼不同的編程語言都可以遵循這種操作規範,在多線程場景下訪問主存保證原子性、可見性、有序性。

Java內存模型(Java Memory Model,JMM)即是Java語言對這個操作規範的遵循,JMM規定了所有的變量都存儲在主存中,每個線程都有自己的工作區,線程將使用到的變量從主存中複製一份到自己的工作區,線程對變量的所有操作(讀取、賦值等)都必須在工作區,不同的線程也無法直接訪問對方工作區,線程之間的消息傳遞都需要通過主存來完成。可以把這裏主存類比成計算機內存模型中的主存,工作區類比成計算機內存模型中的高速緩存。

面試官:什麼是Java內存模型?你知道嗎?



而我們知道JMM其實是工作主存中的,Java內存模型中的工作區也是主存中的一部分,所以可以這樣說Java內存模型解決的是內存一致性問題(主存和主存)而計算機內存模型解決的是緩存一致性問題(CPU高速緩存和主存),這兩個模型類似,但是作用域不一樣,Java內存模型保證的是主存和主存之間的原子性、可見性、有序性,而計算機內存模型保證的是CPU高速緩存和主存之間的原子性、可見性、有序性。

面試官:什麼是Java內存模型?你知道嗎?



總結

本文很多觀點都是按照筆者自己的理解然後總結出來的,若有偏頗,歡迎指正!

小編在學習過程中整理了一些學習資料,可以分享給做java的工程師朋友們,相互交流學習,需要的可以加入我的學習交流羣778477315  即可免費獲取Java架構學習資料(裏面有高可用、高併發、高性能及分佈式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)

其中覆蓋了互聯網的方方面面,期間碰到各種產品各種場景下的各種問題,很值得大家借鑑和學習,擴展自己的技術廣度和知識面。最後記得幫作者點個關注


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章