JMM 是抽象的內存規範
對變量有改操作後,會把結果回寫回來。
,先用assign 把執行引擎的數據同步到工作內存中,然後從工作內存store出來,再用write到主內存中。
必須把它的工作內存的數據同步到主內存中
八大
java併發的三大特性:
- 可見性
- 原子性
- 有序性
可見性
每個線程只能看到自己的工作內存的更改, 加一個volatile可以及時的看到,但是不加volatile線程在一定的時間之後也會看到,只是看到的時機確定不了。
volatile是java併發輕量級的鎖機制
線程B改了initFlag後就會主動通知線程A,讓線程A去主內存再拿一次
加了volatile後字節碼層面會給這個initFlag變量加一個ACC_VOLATILE標記
原子性
加了volatile也不能保證原子性,要用synchronized或者lock去保證原子性。
counter++ 相當於分了三步(讀、自加、寫回),這三步每一步是原子的,但是這三步加在一起不能保證原子性。因爲如果執行了一步就被切到另一個線程後,另一個線程對counter修改後因爲加了volatile會通知線程A 去主內存讀新的,那之前執行的就會扔掉 就少加了一次。
有序性
指令重排
我們的java代碼的順序 不是CPU執行指令的順序,他會進行指令重排比如a和b都依賴z變量 ,如果b指令是在很後面 就會被提前到和a指令一起加到緩存去
volatile可以解決指令重排
代碼證明確實有指令重排的情況
-
第一種:x=0,y=1
-
第二種:x=1,y=0
- 第三種 x=1,y=1
- 指令重排則會出現 x=0,y=0
指令重排的危害
內存屏障
volatile 中加了內存屏障
我們也可以自己手動加內存屏障,用Unsafe類的以下三個方法: