先介紹兩個(gè)概念this逃逸和內(nèi)存屏障
this逃逸
一個(gè)小栗子:
步驟1和步驟2之前沒有重排序的限制規(guī)定郭赐,因此這兩個(gè)操作是可以重排序的。如果出現(xiàn)了重排序那么執(zhí)行”read”方法的線程就有可能讀到final變量初始化之前的值,造成final變量未正確初始化确沸。
另外在構(gòu)造函數(shù)中注冊(cè)事件監(jiān)聽捌锭,在構(gòu)造函數(shù)中啟動(dòng)新線程都有可能會(huì)引發(fā)”this逃逸”。
在構(gòu)造函數(shù)返回之前罗捎,其他線程就持有了該對(duì)象的引用观谦,這種情況就叫this逃逸。調(diào)用尚未構(gòu)造完成的對(duì)象的方法可能引發(fā)錯(cuò)誤桨菜。
內(nèi)存屏障指
內(nèi)存屏障(memory barriers)也成為內(nèi)存柵欄豁状,是一組處理器指令捉偏,對(duì)內(nèi)存操作的順序進(jìn)行限制,大多數(shù)現(xiàn)代計(jì)算機(jī)為了提高性能而采取亂序執(zhí)行這使得內(nèi)存屏障成為必須泻红。JMM內(nèi)存語義底層是通過memory barriers實(shí)現(xiàn)的夭禽。
1、LoadLoad Barriers:示意指令"Load1; LoadLoad; Load2"谊路,保證Load1先讀取完畢讹躯,再執(zhí)行Load2及后續(xù)讀取指令。
2缠劝、StoreStore Barriers:示意指令"Store1; StoreStore; Store2"潮梯,保證Store1先刷新到內(nèi)存(對(duì)其他處理器可見),再執(zhí)行Store2及后續(xù)寫入操作惨恭。
3秉馏、LoadStore Barriers:示意指令"Load1; LoadStore; Store2",保證Load1先讀取完畢脱羡,再執(zhí)行Store2及后續(xù)寫入指令刷新到內(nèi)存萝究。
4、StoreLoad Barriers:示意指令"Store1; StoreLoad; Load2"锉罐,保證Store1先刷新到內(nèi)存(對(duì)其他處理器可見)糊肤,再執(zhí)行Load2及后續(xù)讀取指令。
在大多是處理器中氓鄙,會(huì)先執(zhí)行完屏障前的所有讀寫指令馆揉,再執(zhí)行屏障后的內(nèi)存訪問指令。
final語義
重排序
JMM對(duì)final的重排序做了特殊的規(guī)定抖拦,并且在JSR-133做了增強(qiáng)升酣,編譯器和處理器在對(duì)final域進(jìn)行重排序時(shí)候,都要遵循如下規(guī)則:
1态罪、在構(gòu)造函數(shù)內(nèi)對(duì)一個(gè)final域的寫入噩茄,與隨后把這個(gè)被構(gòu)造對(duì)象的引用賦值給一個(gè)引用變量,這兩個(gè)操作之間不能重排序复颈。
禁止編譯器重排序:編譯器不能將final域的寫操作重排序到構(gòu)造函數(shù)之外绩聘;
禁止處理器重排序:編譯器會(huì)在final與寫之后,構(gòu)造函數(shù)return之前插入個(gè)StoreStore屏障耗啦,用來禁止處理器將final域的寫操作重排序到構(gòu)造函數(shù)之外凿菩;
2、初次讀一個(gè)包含final域的對(duì)象的引用帜讲,與隨后初次讀這個(gè)final域衅谷,這兩個(gè)操作之間不能重排序。
讀對(duì)象引用似将,讀對(duì)象的final域 這兩個(gè)操作不能重排序获黔。
3蚀苛、在構(gòu)造函數(shù)內(nèi)對(duì)一個(gè)final引用的對(duì)象的成員域的寫入,與隨后在構(gòu)造函數(shù)外把這個(gè)被構(gòu)造對(duì)象的引用賦值給一個(gè)引用變量玷氏,這兩個(gè)操作之間不能重排序堵未。如下代碼s1處和s2禁止進(jìn)行重排序:
可見性
final修飾的變量作為不可變變量,只要對(duì)象是正確構(gòu)造的(沒有this逃逸發(fā)生)盏触,不需要任何同步措施就可以保證任何線程都能讀到變量在構(gòu)造函數(shù)中被初始化之后的值兴溜。
volatile語義
原子性
volatile是否保障原子性是個(gè)有爭(zhēng)議的話題,32位的JDK中volatile修飾后的long和double也具有原子性耻陕。但是volatile int i=0;i++;就不具備原子性,因此可以理解為對(duì)單次volatile變量的讀寫操作具有原子性刨沦,復(fù)合操作(如i++)不具有原子性诗宣。
重排序
為了實(shí)現(xiàn)volatile的語義,JMM限制了volatile的編譯器重排序和處理器重排序想诅,volatile變量編譯器重排序規(guī)則:
編譯器會(huì)在volatile寫操作前后插入StoreStore Barriers和StoreLoad Barriers召庞,volatile讀操作前后插入LoadLoad Barriers和LoadStore Barriers。通過插入內(nèi)存屏障來限制處理器對(duì)volatile進(jìn)行重排序来破。
volatile與可見性
通過volatile讀寫操作前后所插入的memory barriers就能夠看出篮灼,volatile變量能夠保證可見性。
小結(jié)
1徘禁、JMM保證final變量初始化時(shí)的有序性诅诱、禁止編譯器和處理器重排序。
2送朱、final作為不可變對(duì)象娘荡,正確初始化后(沒有this逃逸),能夠保障可見性驶沼。
3炮沐、volatile能夠保障單次操作的原子性
4、volatile能夠保障變量的可見性
碼字不易回怜,轉(zhuǎn)載請(qǐng)保留原文連接http://www.reibang.com/p/aa432a918db9