并發(fā)編程有三個(gè)基本概念:
(1)原子性
即一個(gè)操作或者多個(gè)操作 要么全部執(zhí)行并且執(zhí)行的過(guò)程不會(huì)被任何因素打斷,要么就都不執(zhí)行。
在java中原子性操作包括以下幾類:
1. 基本類型的讀取和賦值操作,且賦值必須是數(shù)字賦值給變量,變量之間的相互賦值不是原子性操作
2. 所有引用reference的賦值操作
3. java.concurrent.Atomic.* 包中所有類的一切操作
(2)可見(jiàn)性
指當(dāng)多個(gè)線程訪問(wèn)同一個(gè)變量時(shí),一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看得到修改的值。
在多線程環(huán)境下,一個(gè)線程對(duì)共享變量的操作對(duì)其他線程是不可見(jiàn)的。Java提供了volatile來(lái)保證可見(jiàn)性,當(dāng)一個(gè)變量被volatile修飾后,表示著線程本地內(nèi)存無(wú)效,當(dāng)一個(gè)線程修改共享變量后他會(huì)立即被更新到主內(nèi)存中,其他線程讀取共享變量時(shí),會(huì)直接從主內(nèi)存中讀取。當(dāng)然,synchronize和Lock都可以保證可見(jiàn)性。synchronized和Lock能保證同一時(shí)刻只有一個(gè)線程獲取鎖然后執(zhí)行同步代碼,并且在釋放鎖之前會(huì)將對(duì)變量的修改刷新到主存當(dāng)中。因此可以保證可見(jiàn)性。
(3)有序性
即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。
在Java內(nèi)存模型中,為了效率是允許編譯器和處理器對(duì)指令進(jìn)行重排序,當(dāng)然重排序不會(huì)影響單線程的運(yùn)行結(jié)果,但是對(duì)多線程會(huì)有影響。Java提供volatile來(lái)保證一定的有序性。最著名的例子就是單例模式里面的DCL(雙重檢查鎖)。另外,可以通過(guò)synchronized和Lock來(lái)保證有序性,synchronized和Lock保證每個(gè)時(shí)刻是有一個(gè)線程執(zhí)行同步代碼,相當(dāng)于是讓線程順序執(zhí)行同步代碼,自然就保證了有序性。
ThreadLocal
ThreadLocal是線程局部變量,是線程內(nèi)獨(dú)享的,在進(jìn)程中定義的一個(gè)變量需要在每個(gè)線程內(nèi)部獨(dú)自操作,互相隔離。
實(shí)現(xiàn)原理:
線程Thread內(nèi)部有個(gè)ThreadLocalMap變量,首先根據(jù)當(dāng)前線程對(duì)象get到該線程的threadLocalMap,這個(gè)map中定義了Entry,用于存儲(chǔ)每個(gè)threadLocal對(duì)象與對(duì)應(yīng)的泛型T的值(或?qū)ο螅?br>
Thread ——> ThreadLocalMap ——> {ThreadLocal對(duì)象 -> T}
一個(gè)線程擁有一個(gè)ThreadLocalMap,但一個(gè)ThreadLocalMap中可以存多個(gè)線程的局部變量。
ThreadLocal的應(yīng)用場(chǎng)景:
(1)在一個(gè)多線程中,每個(gè)線程獨(dú)享變量
(2)參數(shù)傳遞,有點(diǎn)spark中廣播變量的意思
Volatile
Volatile是修飾符關(guān)鍵字,用來(lái)修飾變量(該變量是線程共享變量),一旦一個(gè)變量被volatile修飾意味著三點(diǎn):
(1)一個(gè)線程對(duì)于該變量的修改,對(duì)于其他線程都是可見(jiàn)的,即所有線程對(duì)于該變量的讀操作都是直接從主存中讀取,而線程對(duì)變量的修改也是強(qiáng)制刷新到主存中的;
(2)禁止編譯器的重排序,對(duì)于該變量的操作前和操作后的代碼不能變序。即當(dāng)程序執(zhí)行到volatile變量的讀操作或者寫操作時(shí),在其前面的操作的更改肯定全部已經(jīng)進(jìn)行,且結(jié)果已經(jīng)對(duì)后面的操作可見(jiàn);在其后面的操作肯定還沒(méi)有進(jìn)行。
(3)Volatile只保證變量的可見(jiàn)性,不保證針對(duì)該變量操作的原子性。