Java內(nèi)存模型
Java內(nèi)存模型定義了線程和主存之間的抽象關(guān)系:線程之間的共享變量存儲(chǔ)在主存中,每個(gè)線程都有一個(gè)私有的本地內(nèi)存,本地內(nèi)存中存儲(chǔ)了該線程共享變量的副本。
Java內(nèi)存模型控制線程之間的通信,它決定一個(gè)線程對(duì)主存共享變量的寫入何時(shí)對(duì)另外一個(gè)線程可見。
Java中的堆內(nèi)存用來存儲(chǔ)對(duì)象實(shí)例,堆內(nèi)存是被所有線程共享的運(yùn)行時(shí)內(nèi)存區(qū)域,因此它存在內(nèi)存可見性的問題。而局部變量、方法定義的參數(shù)則不會(huì)在線程之間共享,它們不會(huì)有內(nèi)存可見性問題,也不受內(nèi)存模型影響。
Java內(nèi)存模型的抽象示意圖:

兩線程通信,必須要經(jīng)歷下面兩個(gè)步驟:
- 線程A把線程A本地內(nèi)存中更新過的共享變量刷新到主內(nèi)存中。
- 線程B到主存中去讀取線程A之前已經(jīng)更新過的共享變量。由此可見,如果我們執(zhí)行下面的語(yǔ)句:
int i3;
執(zhí)行線程必須先在自己的工作線程中對(duì)變量所在的緩存進(jìn)行賦值操作,然后再寫入主存當(dāng)中,而不是直接將數(shù)值3寫入主存當(dāng)中。
線程安全的三個(gè)性質(zhì)
原子性
對(duì)基本數(shù)據(jù)類型的讀取和賦值操作是原子性操作,即這些操作是不可被中斷的,要么執(zhí)行完畢,要么不執(zhí)行。
注意補(bǔ)充:賦值操作必須是將基本數(shù)據(jù)類型賦值給某個(gè)變量,變量之間的相互賦值不是原子操作)
例子:
x = 10; //語(yǔ)句1
y = x; //語(yǔ)句2
x++; //語(yǔ)句3
x = x + 1; //語(yǔ)句4
只有語(yǔ)句1是原子性操作,其他三個(gè)語(yǔ)句都不是原子性操作。
語(yǔ)句1是直接將數(shù)值10賦值給x,也就是說線程執(zhí)行這個(gè)語(yǔ)句的會(huì)直接將數(shù)值10寫入到工作內(nèi)存中。
語(yǔ)句2實(shí)際上包含2個(gè)操作,它先要去讀取x的值,再將x的值寫入工作內(nèi)存,雖然讀取x的值以及 將x的值寫入工作內(nèi)存 這2個(gè)操作都是原子性操作,但是合起來就不是原子性操作了。
同樣的,x++和 x = x+1包括3個(gè)操作:讀取x的值,進(jìn)行加1操作,寫入新的值。
可以有以下結(jié)論:
1.并不是一個(gè)語(yǔ)句表示原子性操作,需要看這個(gè)語(yǔ)句中有多少個(gè)操作。
java.util.concurrent.atomic包中提供很多類使用了很高效的機(jī)器級(jí)指令(而不是使用鎖)來保證其他操作的原子性。例如:AtomicInteger類提供了方法incrementAndGet和decrementAndGet,它們分別以原子方式將一個(gè)整數(shù)自增和自減??梢园踩厥褂肁tomicInteger類作為共享計(jì)數(shù)器而無需同步。另外這個(gè)包還有AtomicBoolean、AtomicLong和AtomicReference這些原子類。
可見性
可見性,是指線程之間的可見性,一個(gè)線程修改的狀態(tài)對(duì)另一個(gè)線程是可見的。也就是一個(gè)線程修改的結(jié)果,另一個(gè)線程馬上就能看到。
對(duì)于可見性,Java提供了volatile關(guān)鍵字來保證可見性。
當(dāng)一個(gè)共享變量被volatile修飾時(shí),它會(huì)保證修改的值立即被更新到主存,所以對(duì)其它線程是可見的。當(dāng)其他線程需要讀取該值時(shí),其它線程會(huì)去主存中讀取新值。而普通的共享變量不能保證可見性,因?yàn)槠胀ü蚕碜兞勘恍薷闹螅⒉粫?huì)立即寫入主存,何時(shí)被寫入主存也是不確定的。當(dāng)其他線程去讀取該值時(shí),此時(shí)主存中可能還是原來的舊值,這樣就無法保證可見性。
有序性
Java內(nèi)存模型中允許編譯器和處理器對(duì)指令進(jìn)行重排序,雖然重排序過程不會(huì)影響到單線程執(zhí)行的正確性,但是會(huì)影響到多線程并發(fā)執(zhí)行的正確性。這是可以通過volatile來保證有序性。
除了volatile,也可以通過synchronized和Lock來保證有序性。它們兩保證每個(gè)時(shí)刻只有一個(gè)線程執(zhí)行同步代碼,這相當(dāng)于是讓線程順序執(zhí)行同步代碼,從而保證了有序性。