Java內存模型的主要目標是定義程序中各個變量的訪問規則,即在虛擬機中將變量存儲到內存和在內存中取出變量的底層細節,是圍繞着在併發過程當中如何處理原子性,可見性和有序性這3個特性創建的java
定義一個靜態變量:static int a = 1;
線程A工做內存 | 指向 | 主內存 | 操做
-- | -- | -- | --
-- | -- | a = 1 | --
a = 1 | <-- | a = 1 | 線程A拷貝主內存變量副本
a = 3 | -- | a = 1 | 線程A修改工做內存變量值
a = 3 | --> | a = 3 | 線程A工做內存變量存儲到主內存變量數組
上面的一系列內存操做,在JMM中定義了8種操做來完成安全
主內存和工做內存之間的交互,JMM定義了8種操做來完成,每一個操做都是原子性的多線程
當引入線程B的時候
定義一個靜態變量:static int a = 1;
操做順序 | 線程A工做內存 | 線程B工做內存 | 指向 | 主內存 | 操做
-- | -- | -- | -- | -- | --
-- | -- | -- | -- | a = 1 | --
1 | a = 1 | -- | <-- | a = 1 | 線程A拷貝主內存變量副本
2 | a = 3 | -- | -- | a = 1 | 線程A修改工做內存變量值
3 | a = 3 | -- | --> | a = 1 | 線程A工做內存變量存儲到主內存變量,主內存變量還未更新
4.1 | a = 3 | a = 1 | <-- | a = 3 |線程B拷貝主內存變量副本隨後主內存變量更新線程A工做內存變量
4.2 | a = 3 | a = 1 | <-- | a = 3 |線程A工做內存變量存儲到主內存變量隨後線程B獲取主內存變量副本
操做4的時候可能出現:1.線程A變量值還未保存到主內存變量,2.線程A變量值保存到主內存變量。使用volatile關鍵字解決這個問題併發
public static volatile int a = 1;
修改內存變量後馬上同步到主內存中,其餘的線程馬上得知得益於Java的先行發生原則app
先行發生原則中的volatile原則:一個volatile變量的寫操做先行於後面發生的這個變量的讀操做,時間順序ide
定義一個靜態變量:static int a = 1;
線程A工做內存 | 線程B工做內存 | 指向 | 主內存 | 操做
-- | -- | -- | -- | --
-- | -- | -- | a = 1 | --
a = 1 | -- | <-- | a = 1 | 線程A拷貝主內存變量副本
a = 3 | -- | -- | a = 1 | 線程A修改工做內存變量值
a = 3 | -- | --> | a = 1 | 線程A工做內存變量存儲到主內存變量
a = 3 | a = 3 | <-- | a = 3 | volatile原則:主內存變量保存線程A工做內存變量操做在線程B工做內存讀取主內存變量操做以前優化
指令重排序:JVM在編譯Java代碼的時候或者CPU在執行JVM字節碼的時候,對現有的指令進行從新排序,目的是爲了再不影響最終結果的前提下,優化程序的執行效率this
內存屏障:一種屏障指令,讓CPU或比編譯器對屏蔽指令以前和以後發出的內存操做執行一個排序約束。
編譯器在生成字節碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序。atom
public class VolatileTest implements Runnable { public static volatile int num; @Override public void run() { for (int i = 0; i < 1000; i++) { num++; } } public static void main(String[] args) { for(int i = 0; i < 100; i++) { VolatileTest t = new VolatileTest(); Thread t0 = new Thread(t); t0.start(); } System.out.println(num); } }
這段代碼的結果有可能不是100000,有可能小於100000。
由於num++不是原子操做
一個操做是不可中斷的。即便是在多個線程一塊兒執行的時候,一個操做一旦開始,就不會被其它線程干擾
當一個線程修改了共享變量的值,其餘線程當即可知
程序執行的順序按照代碼的前後順序執行,在Java內存模型中,容許編譯器和處理器對指令進行重排序,可是重排序過程不會影響到單線程程序的執行,卻會影響到多線程併發執行的正確性
JMM中兩項操做之間的偏序關係,若是操做A發生於操做B以前,操做A發生的影響能夠被操做B觀察到
單線程和正確同步的多線程的執行結果不會被改變
若是兩個操做不在下列規則中,虛擬機能夠對其重排序