Java內(nèi)存模型
Java內(nèi)存模型的主要目標(biāo)是定義程序中各個(gè)變量的訪問(wèn)規(guī)則喊式,即JVM中將變量存儲(chǔ)到內(nèi)存中和從內(nèi)存中取出變量這樣的底層細(xì)節(jié)锌奴,變量包括了實(shí)例字段兽狭、靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素,但是不包括局部變量和方法參數(shù),內(nèi)存模型規(guī)定了所有的變量都必須存儲(chǔ)在主內(nèi)存箕慧,線程對(duì)變量的所有操作(取值服球、賦值)都必須在內(nèi)存中完成。
內(nèi)存模型定義了8種操作來(lái)完成工作內(nèi)存和主內(nèi)存之間的實(shí)現(xiàn)細(xì)節(jié)颠焦,而且這幾種操作都是原子的斩熊、不可再分的(64位的double和long類型除外),線程伐庭、工作內(nèi)存和主內(nèi)存之間交互如下圖所示:
lock(鎖定):作用于主內(nèi)存的變量,把一個(gè)變量標(biāo)識(shí)為一條線程獨(dú)占的狀態(tài)圾另。
unlock(解鎖):作用于主內(nèi)存的變量,將一個(gè)鎖定狀態(tài)的變量釋放出來(lái)霸株,釋放的變量可由其他線程鎖定。
read(讀燃恰):作用于主內(nèi)存的變量去件,把一個(gè)變量從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中,以便隨后的load使用饺著。
load(載入):作用于工作內(nèi)存的變量箫攀,把read操作從主內(nèi)存中讀取的變量放入工作內(nèi)存的變量副本中。
use(使用):作用于工作內(nèi)存的變量幼衰,把工作內(nèi)存中的值傳遞給執(zhí)行引擎靴跛,每當(dāng)jvm遇到一個(gè)需要使用變量的值的字節(jié)碼指令就執(zhí)行這個(gè)操作。
assign(賦值):作用于工作內(nèi)存的變量,把執(zhí)行引擎接收到的值賦給工作內(nèi)存中的變量渡嚣,每當(dāng)jvm遇到一個(gè)需要給變量賦值的字節(jié)碼指令就執(zhí)行這個(gè)操作梢睛。
store(存儲(chǔ)):作用于工作內(nèi)存的變量,把工作內(nèi)存中的值傳輸?shù)街鲀?nèi)存中,供之后的write操作使用识椰。
write(寫(xiě)入):作用于主內(nèi)存的變量,把store操作傳入的變量的值放入主內(nèi)存中的變量中绝葡。
主內(nèi)存->工作內(nèi)存 read->load
工作內(nèi)存->主內(nèi)存 store->write
上述操作必須按順序執(zhí)行,但是并不一定需要連續(xù)執(zhí)行腹鹉。
針對(duì)以上操作需滿足以下幾個(gè)規(guī)則:
(1)不允許read,load,store和write單獨(dú)出現(xiàn)
(2)不允許一個(gè)線程丟棄最近的assign操作
(3)一個(gè)新變量只能在內(nèi)存中誕生
(4)一個(gè)變量在同一個(gè)時(shí)刻只允許一個(gè)線程進(jìn)行l(wèi)ock操作
(5)不允許一個(gè)線程沒(méi)有assign操作將數(shù)據(jù)從工作內(nèi)存同步回主內(nèi)存
(6)如果線程對(duì)一個(gè)變量進(jìn)行l(wèi)ock操作藏畅,將會(huì)把工作內(nèi)存中的數(shù)據(jù)清空。功咒,需要重新load或assgin變量的值愉阎。
(7)對(duì)一個(gè)變量進(jìn)行unlock操作之前,必須將數(shù)據(jù)從工作內(nèi)存同步回主內(nèi)存(執(zhí)行store力奋,write操作)
對(duì)volatile變量的特殊規(guī)則
JVM提供的最輕量級(jí)的同步機(jī)制榜旦,它具備以下2種特性:
(1)保證此變量對(duì)所有線程的可見(jiàn)性,但并不保證并發(fā)安全景殷,java里面的運(yùn)算并非原子操作溅呢,所以volatile修飾的變量在并發(fā)條件下不能保證線程安全澡屡。
底層原理:java的++操作,編譯后由4條字節(jié)碼指令(多條機(jī)器碼指令)構(gòu)成:
a.getstatic
b.iconst_1
c.iadd
d.putstatic
在執(zhí)行iconst_1,iadd指令時(shí)咐旧,可能其他線程已經(jīng)修改了操作棧頂?shù)闹凳火模瑢?dǎo)致pustatic執(zhí)行時(shí),把已經(jīng)過(guò)期的數(shù)據(jù)(比實(shí)際數(shù)據(jù)行菖肌)同步回主內(nèi)存梁厉。
演示代碼:
public class VolatileTest {
private static volatile int count = 0;
public static void main(String[] args) {
VolatileTest volatileTest = new VolatileTest();
for (int i = 0; i < 10000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
count++;
}
}).start();
}
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println("累加結(jié)果:" + count);
}
}
反編譯后的++操作字節(jié)碼:
預(yù)期結(jié)果應(yīng)該是10000,但是實(shí)際結(jié)果是9996踏兜,說(shuō)明在累加過(guò)程中把過(guò)期的值同步回了主內(nèi)存。
volatile的使用場(chǎng)景:
a.運(yùn)算結(jié)果不依賴變量當(dāng)前計(jì)算的值或者能夠確保只有單一線程能夠修改線程的值
b.變量不需要與其他狀態(tài)變量共同參與不變約束(只需要一個(gè)變量就可以控制并發(fā))
(2)禁止指令重排序優(yōu)化
有volatile修飾的變量八秃,賦值后多執(zhí)行了一個(gè)"lock"操作碱妆,這個(gè)操作相當(dāng)于一個(gè)內(nèi)存屏障(指令重排序時(shí)不能將后面的指令重排序到內(nèi)存屏障之前的位置),該指令使得本cpu的cache寫(xiě)入內(nèi)存昔驱,同時(shí)會(huì)引起其他CPU或者別的內(nèi)核無(wú)效化其cache,相當(dāng)于做了"store和write操作"疹尾,因而使得volatile修飾的變量的修改對(duì)其他CPU立即可見(jiàn)。
線程并發(fā)過(guò)程中的原子性骤肛、有序性和可見(jiàn)性
原子性:基本數(shù)據(jù)類型的訪問(wèn)讀寫(xiě)是具備原子性的纳本,原子性變量操作包括read、load腋颠、assign繁成、use、store和write.
(1)synchroniozed關(guān)鍵字淑玫,底層由字節(jié)碼指令monitorenter和monitorexit實(shí)現(xiàn)巾腕。
(2)lock和unlock操作
可見(jiàn)性:當(dāng)一個(gè)線程修改了共享變量的值,其他線程能夠立即得知這個(gè)修改絮蒿。
有序性:在本線程中觀察尊搬,所有操作都是有序的;如果在一個(gè)線程中觀察另外一個(gè)線程土涝,所有操作都是無(wú)序的佛寿。
Java與線程
(1)線程的實(shí)現(xiàn)
a.使用內(nèi)核線程實(shí)現(xiàn)
直接由操作系統(tǒng)內(nèi)核支持的線程,內(nèi)核通過(guò)調(diào)度器對(duì)線程進(jìn)行調(diào)度但壮,將線程任務(wù)反映到各個(gè)處理器上.
b.使用用戶線程實(shí)現(xiàn)
不是內(nèi)核線程的線程
c.使用用戶線程和輕量級(jí)進(jìn)程實(shí)現(xiàn)
輕量級(jí)進(jìn)程由內(nèi)核線程支持冀泻,內(nèi)核線程與輕量級(jí)進(jìn)程是1:1的關(guān)系,進(jìn)程與用戶線程是1:N的關(guān)系,輕量級(jí)進(jìn)程與用戶線程是N:M的關(guān)系;SunJDK使用1:1的線程模型實(shí)現(xiàn)茵肃。
(2)Java線程調(diào)度
a.協(xié)同式線程調(diào)度
線程的執(zhí)行時(shí)間由自己控制腔长,線程把工作完成之后,要主動(dòng)通知操作系統(tǒng)切換到另外一個(gè)線程验残,缺點(diǎn)是一個(gè)線程阻塞會(huì)導(dǎo)致整個(gè)進(jìn)程阻塞捞附,好處是實(shí)現(xiàn)簡(jiǎn)單。
b.搶占式線程調(diào)度
每個(gè)線程由系統(tǒng)來(lái)分配執(zhí)行時(shí)間,JAVA使用搶占式調(diào)度鸟召,通過(guò)設(shè)置線程優(yōu)先級(jí)(1-10)可由設(shè)置指定線程分配更多執(zhí)行時(shí)間胆绊,優(yōu)先級(jí)高的線程會(huì)被操作系統(tǒng)優(yōu)先執(zhí)行。
(3)狀態(tài)切換
Java語(yǔ)言定義了5種線程狀態(tài).
