java內存模型

java內存模型(JMM)是一種規範,定義了程序中變量的訪問規則,目的是解決因爲多線程經過共享內存進行通訊時,由工做內存數據不一致、編譯器指令重排序、處理器優化等帶來的原子性、有序性和緩存一致性等問題。
在多核CPU的環境下,多線程分別在不一樣的核心上執行,當多個線程訪問進程中的某個共享內存時,每一個核心都會在各自的CPU緩存中保留一份共享內存的拷貝,因爲多核是能夠並行的,可能會出現多個線程同時寫各自的緩存的狀況,而各自cache之間的數據就有可能不一樣。
Java內存模型分爲主內存和工做內存,同時模型規定了全部的變量(共享變量)都存儲在主內存中,每條線程有本身的工做內存,線程的工做內存中保存的變量是主內存中的變量的一個副本,線程對變量的全部操做都必須在本身的工做內存中進行,不能直接讀寫主內存。不一樣的線程之間也沒法直接訪問對方工做內存中的變量,線程間變量的傳遞均須要本身的工做內存和主存之間進行數據同步進行。
java內存模型要解決的就是工做內存與主內存之間的數據同步問題。java中的volatile、synchronized、final以及concurren包等就是解決這個問題的,
java中的synchronized關鍵字經過monitorenter和monitorexit兩個字節碼指令實現加鎖解鎖操做,保證同一時刻只有一個線程能獲取鎖而後執行同步代碼,而且在釋放鎖以前會將對變量的修改刷新到主存中,從而保證原子性、可見性和有序性。
volatile關鍵字能夠保證變量的可見性 和 禁止指令重排序優化。被volatile修飾的變量在被修改後會當即同步到主內存,並將其餘線程中的這個變量副本置失效,其餘線程在訪問這個變量以前都從主內存中刷新。同時,對應volatile變量,Java內存模型會在寫操做後插入一個寫屏障指令,在讀操做前插入一個讀屏障指令,內存屏障可以阻止屏障兩側的指令重排序,從而保證了程序的有序性,另外內存屏障還會強制更新一次不一樣CPU的緩存。java

ps:volatile關鍵字僅能實現對原始變量(如boolen、 short 、int 、long等)操做的原子性,但須要特別注意, volatile不能保證複合操做的原子性,即便只是i++,實際上也是由多個原子操做組成:read i; inc; write i,假如多個線程同時執行i++,volatile只能保證他們操做的i是同一塊內存,但依然可能出現寫入髒數據的狀況。
對於64位的long和double,若是沒有被volatile修飾,那麼對其操做能夠不是原子的。在操做的時候,能夠分紅兩步,每次對32位操做。
若是使用volatile修飾long和double,那麼其讀寫都是原子操做
對於64位的引用地址的讀寫,都是原子操做緩存

volatile和synchronized的區別

1.volatile僅能使用在變量級別; synchronized則可使用在變量、方法、和類級別的2.volatile僅能實現變量的修改可見性,並不能保證原子性;synchronized則能夠保證變量的修改可見性和原子性3.volatile不會形成線程的阻塞; synchronized可能會形成線程的阻塞。4.volatile標記的變量不會被編譯器優化;synchronized標記的變量能夠被編譯器優化。多線程

使用volatile必須具有如下2個條件:

1)對變量的寫操做不依賴於當前值
2)該變量沒有包含在具備其餘變量的不變式中jvm

synchronized與lock的區別

  • synchronized是java中的關鍵字,是基於jvm實現的,屬於內置鎖,java中的每個對象均可以做爲鎖;lock是接口,是基於語言層面實現的的鎖。
  • synchronized在發生異常時,會自動釋放線程佔有的鎖,不會發生死鎖;而lock在發生異常時,若是沒有主動調用unLock()來釋放鎖就有可能形成死鎖,所以在使用Lock時須要在finally塊中釋放鎖。
  • Lock可讓等待說的線程響應中斷,而synchronized不能。
  • Lock能夠判斷是否成功獲取鎖,而synchronized不能。
  • lock能夠是公平的,也但是非公平的,而synchronized只能是非公平的。
  • synchronized關鍵字與wait()和notify/notifyAll()方法相結合能夠實現等待/通知機制,被通知的線程是由 JVM 選擇的,用ReentrantLock類結合Condition實例能夠實現「選擇性通知」。
相關文章
相關標籤/搜索