參考:海子的博客Java并發(fā)編程:volatile關(guān)鍵字解析
三個(gè)重點(diǎn):
- 原子性
- 可見性
- 有序性
討論這三點(diǎn)之前先說一下計(jì)算機(jī)的內(nèi)存模型:
-
CPU對(duì)程序的指令的執(zhí)行速度遠(yuǎn)遠(yuǎn)大于從內(nèi)存中讀寫數(shù)據(jù)的速度
所以如果讓CPU直接訪問內(nèi)存來讀數(shù)據(jù)再寫結(jié)果去內(nèi)存,就會(huì)效率非常低下绩社。因此就有了高速緩存——Cache這個(gè)東西炸宵。
CPU和內(nèi)存之間存在高速緩存产上,他們之間的工作關(guān)系 - 舉個(gè)例子看看他們?cè)趺垂ぷ鞯?
i=0;
單線程運(yùn)行代碼:i=i+1;
步驟是:Cache從主存讀取i=0
隘马, CPU從Cache讀取i,CPU運(yùn)算i=i+1队腐, 將結(jié)果i=1
寫入Cache坐儿,Cache再將結(jié)果i=1
寫入主存。
單線程沒問題贝乎,但是多線程中情连,比如開啟兩個(gè)線程分別執(zhí)行i=i+1;
,我們希望結(jié)果是i=2
但是結(jié)果可能會(huì)是這樣:
線程1的cache從內(nèi)存讀i=0,這時(shí)览效,沒有馬上計(jì)算却舀,CPU馬上換到線程2讀取,這是i仍然是0锤灿,然后線程1和線程2的cache再分別把i=0給cpu進(jìn)行計(jì)算都得到i=1挽拔,然后分別刷新到主存中,最后的結(jié)果是i=1
因此
出現(xiàn)了緩存一致性協(xié)議但校,意思是:當(dāng)CPU寫數(shù)據(jù)時(shí)螃诅,如果發(fā)現(xiàn)操作的變量是共享變量,即在其他CPU中也存在該變量的副本状囱,會(huì)發(fā)出信號(hào)通知其他CPU將該變量的緩存行置為無效狀態(tài)术裸,因此當(dāng)其他CPU需要讀取這個(gè)變量時(shí),發(fā)現(xiàn)自己緩存中緩存該變量的緩存行是無效的亭枷,那么它就會(huì)從內(nèi)存重新讀取袭艺。
現(xiàn)在進(jìn)入正題:
java內(nèi)存模型:
Java內(nèi)存模型規(guī)定所有的變量都是存在主存當(dāng)中,每個(gè)線程都有自己的工作內(nèi)存(類似于前面的高速緩存)叨粘。線程對(duì)變量的所有操作都必須在工作內(nèi)存中進(jìn)行猾编,而不能直接對(duì)主存進(jìn)行操作。并且每個(gè)線程不能訪問其他線程的工作內(nèi)存升敲。
圖示:
- 原子性:
- 在Java中袍镀,對(duì)基本數(shù)據(jù)類型變量的讀取和賦值操作是原子性操作,即這些操作是不可被中斷的冻晤,要么執(zhí)行苇羡,要么不執(zhí)行。
- 只有簡單的讀取鼻弧、賦值(而且必須是將數(shù)字賦值給某個(gè)變量设江,變量之間的相互賦值不是原子操作)才是原子操作锦茁。
x = 10; //語句1
y = x; //語句2
x++; //語句3
x = x + 1; //語句4
哪幾個(gè)是原子操作?只有語句一是原子性操作叉存;其他操作除了讀取之外都有賦值码俩,因此不是原子性。
如果要實(shí)現(xiàn)大范圍的原子性操作歼捏,就要用同步稿存。
- 可見性:
在CPU讀寫數(shù)據(jù)的時(shí)候,不同線程之間對(duì)變量的更行可能沒有及時(shí)刷新到內(nèi)存中去瞳秽,因此其他線程不能立刻看到修改后的變量瓣履,這是不可見的。java中提供了兩種實(shí)現(xiàn)可見性的方法:
1练俐、volatile關(guān)鍵字:當(dāng)一個(gè)變量被volatile修飾時(shí)袖迎,它會(huì)保證修改的值會(huì)立即被更新到主存,當(dāng)有其他線程需要讀取時(shí)腺晾,它會(huì)去內(nèi)存中讀取新值燕锥。而普通的共享變量不能保證可見性,因?yàn)槠胀ü蚕碜兞勘恍薷闹竺醪酰裁磿r(shí)候被寫入主存是不確定的归形,當(dāng)其他線程去讀取時(shí),此時(shí)內(nèi)存中可能還是原來的舊值鼻由,因此無法保證可見性连霉。
2、同步:synchronized和Lock能保證同一時(shí)刻只有一個(gè)線程獲取鎖然后執(zhí)行同步代碼嗡靡,并且在釋放鎖之前會(huì)將對(duì)變量的修改刷新到主存當(dāng)中。因此可以保證可見性窟感。 - 有序性:
為了提高程序的運(yùn)行效率讨彼,沒有數(shù)據(jù)依賴性的代碼段可能會(huì)被處理器重新排序,也叫指令重排序柿祈。(但是有數(shù)據(jù)依賴性的代碼就不會(huì)重排序)