1.內(nèi)存模型(JMM)
1.1什么是Java內(nèi)存模型?
Java內(nèi)存模型將內(nèi)存分為主內(nèi)存和工作內(nèi)存兩大部分;主內(nèi)存用來存儲線程之間共享數(shù)據(jù),工作內(nèi)存則是每個線程獨享內(nèi)存,存儲對應線程使用到的共享數(shù)據(jù)副本

Java內(nèi)存模型和硬件中的多核CPU、CPU對應的緩存(1級緩存、2級緩存、3級緩存)、以及內(nèi)存之間的設計異曲同工;CPU的每個核可以跑不通的線程,由于CPU執(zhí)行指令速度遠遠大于內(nèi)存讀寫速度,為了平衡兩者差距,高速緩存就引入了計算機體系中,高速緩存位于CPU和內(nèi)存之間;讀寫速度能力 CPU>L1>L2>L3>Memory(越靠近CPU側(cè),處理能力越高,讀寫越快);存儲數(shù)據(jù)能力Memory>L3>L2>L1>CPU(越靠近Memory側(cè),存儲能力越強);計算機執(zhí)行程序時,CPU執(zhí)行程序時,會頻繁的使用數(shù)據(jù)從內(nèi)存中拷貝到緩存中,這樣CPU運算時可以更快的讀取到運算的數(shù)據(jù);當CPU運算結束,又將數(shù)據(jù)從高速緩存寫回到內(nèi)存中。
1.2.內(nèi)存模型中工作內(nèi)存和主內(nèi)存的區(qū)別?
- 主內(nèi)存:所有線程共享的區(qū)域;存儲共享數(shù)據(jù)的區(qū)域
- 工作內(nèi)存:線程私有的存儲區(qū)域;主要存儲線程使用的主內(nèi)存中數(shù)據(jù)的副本
1.3.內(nèi)存模型帶來什么問題
工作內(nèi)存和主內(nèi)存中數(shù)據(jù)不一致問題
-
原子性
線程1 和 線程2 并發(fā)執(zhí)行 +1操作,并同步到主內(nèi)存中

-
可見性
內(nèi)存模型-可見性.png
- 重排序
public class Test{
private int a = 0;
private int b = 0;
public void setAAndB() {
a = 1;
b = 2;
}
public void changeB() {
if (a == 1) {
b = 4;
}
System.out.println("b=" + b);
}
}

1.4.volatile關鍵字有哪些語義
volatile主要面對JMM帶來的問題(工作內(nèi)存和主內(nèi)存中數(shù)據(jù)不一致)而產(chǎn)生的一種解決方案
語義有
- volatile修飾的變量具有可見性
- volatile修飾的變量讀寫原子性(如32位系統(tǒng)上的Long變量)
- volatile修飾的變量,讀寫操作不會參與前后指令的重排序
具體理解
-
volatile修飾的變量具有可見性
對于一個volatile變量的讀,總能看到(任意線程)對這個volatile變量最后的寫入值
寫:當寫一個volatile變量時,JMM會把該線程對應的本地內(nèi)存中的共享變量值刷新到主內(nèi)存中
讀:當讀一個volatile變量時,JMM會把該線程對應的本地內(nèi)存置為無效,重新從主內(nèi)存中讀取共享變量到工作內(nèi)存中
volatile可見性.png
-
volatile修飾的變量讀寫原子性(單步操作)
對于任意單個volatile變量的讀、寫具有原子性;但是對于i++這種復合操作不具有原子性
-
volatile修飾的變量,讀寫操作不會參與前后指令的重排序
是否可以重排序 第二個操作 第二個操作 第二個操作 第一個操作 普通讀/寫 volatile讀 volatile寫 普通讀/寫 不可以重排序 volatile讀 不可以重排序 不可以重排序 不可以重排序 volatile寫 不可以重排序 不可以重排序 總結:
- 第一個操作為volatile讀時,不管第二個操作是什么,都不可以重排序
- 第二個操作為volatile寫時,不管第一個操作是什么,都不可以重排序
- 第一個操作為volatile寫,第二個操作為volatile讀時,不可以重排序
1.5.volatile是怎么實現(xiàn)的
還是圍繞三個語義來看
-
可見性
在寫入volatile變量的時候,JVM會向CPU發(fā)送Lock指令,Lock指令具有兩個作用
- 將當前工作內(nèi)存中數(shù)據(jù)寫入到主內(nèi)存中
- 涉及其他線程對應的CPU核中緩存的數(shù)據(jù)設置無效(CPU消息一致性協(xié)議和嗅探功能),之后讀會重新重主內(nèi)存拉取最新數(shù)據(jù)
-
原子性(讀寫原子性)
線程是CPU調(diào)度的基本單位。CPU有時間片的概念,會根據(jù)不同的調(diào)度算法進行線程調(diào)度。當一個線程獲得時間片之后開始執(zhí)行,在時間片耗盡之后,就會失去CPU使用權。所以在多線程場景下,由于時間片在線程間輪換,就會發(fā)生原子性問題。
為了保證原子性,需要通過字節(jié)碼指令monitorenter和monitorexit,但是volatile和這兩個指令之間是沒有任何關系的。
所以,volatile是不能保證(復合操作)原子性的
-
重排序(有序性)
主要靠內(nèi)存屏障(硬件層面的指令,它可以禁止屏障前后的指令進行重排序)
- 在每個volatile寫操作的前面插入一個StoreStore屏障
- 在每個volatile寫操作的后面插入一個StoreLoad屏障
- 在每個volatile讀操作的后面插入一個LoadLoad屏障
- 在每個volatile讀操作的后面插入一個LoadStore屏障
volatile-重排序機制.png


