本文部分概念參考大神“海 子”博客品抽,感謝作者:
http://www.cnblogs.com/dolphin0520/p/3920373.html
1.原子性操作
來個(gè)例子:
張三賬戶有1000大洋坡椒,李四賬戶有2000大洋抒倚,張三要給李四轉(zhuǎn)賬100大洋,在這個(gè)過程中:
if成功:
1.張三賬戶:1000-100=900
2.李四賬戶:2000+100=2100
if失敼娣А:
1.張三賬戶:1000
2.李四賬戶:2000
我們把這種要么同時(shí)成功挣跋,要么同時(shí)失敗要么同時(shí)成功的操作叫做具有原子性的操作。
2.指令重排
天天聽大神說:指令重排悬嗓,指令重排到底是個(gè)什么鬼污呼?
舉個(gè)例子:
int i = 0;
boolean flag = false;
i = 1; //語句1
flag = true; //語句2
上面代碼定義了一個(gè)int型變量,定義了一個(gè)boolean類型變量包竹,然后分別對(duì)兩個(gè)變量進(jìn)行賦值操作燕酷。從代碼順序上看籍凝,語句1是在語句2前面的,那么JVM在真正執(zhí)行這段代碼的時(shí)候會(huì)保證語句1一定會(huì)在語句2前面執(zhí)行嗎苗缩?不一定饵蒂,為什么呢?這里可能會(huì)發(fā)生指令重排序(Instruction Reorder)酱讶。
下面解釋一下什么是指令重排序退盯,一般來說,處理器為了提高程序運(yùn)行效率泻肯,可能會(huì)對(duì)輸入代碼進(jìn)行優(yōu)化得问,它不保證程序中各個(gè)語句的執(zhí)行先后順序同代碼中的順序一致,但是它會(huì)保證程序最終執(zhí)行結(jié)果和代碼順序執(zhí)行的結(jié)果是一致的软免。
比如上面的代碼中宫纬,語句1和語句2誰先執(zhí)行對(duì)最終的程序結(jié)果并沒有影響,那么就有可能在執(zhí)行過程中膏萧,語句2先執(zhí)行而語句1后執(zhí)行漓骚。
但是要注意,雖然處理器會(huì)對(duì)指令進(jìn)行重排序榛泛,但是它會(huì)保證程序最終結(jié)果會(huì)和代碼順序執(zhí)行結(jié)果相同蝌蹂,那么它靠什么保證的呢?再看下面一個(gè)例子:
int a = 10; //語句1
int r = 2; //語句2
a = a + 3; //語句3
r = a*a; //語句4
這段代碼有4個(gè)語句曹锨,那么可能的一個(gè)執(zhí)行順序是:
那么可不可能是這個(gè)執(zhí)行順序呢: 語句2 語句1 語句4 語句3
不可能孤个,因?yàn)樘幚砥髟谶M(jìn)行重排序時(shí)是會(huì)考慮指令之間的數(shù)據(jù)依賴性,如果一個(gè)指令I(lǐng)nstruction 2必須用到Instruction 1的結(jié)果沛简,那么處理器會(huì)保證Instruction 1會(huì)在Instruction 2之前執(zhí)行齐鲤。
雖然重排序不會(huì)影響單個(gè)線程內(nèi)程序執(zhí)行的結(jié)果,但是多線程呢椒楣?下面看一個(gè)例子:
//線程1:
context = loadContext(); //語句1
inited = true; //語句2
//線程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
上面代碼中给郊,由于語句1和語句2沒有數(shù)據(jù)依賴性,因此可能會(huì)被重排序捧灰。假如發(fā)生了重排序淆九,在線程1執(zhí)行過程中先執(zhí)行語句2,而此是線程2會(huì)以為初始化工作已經(jīng)完成毛俏,那么就會(huì)跳出while循環(huán)炭庙,去執(zhí)行doSomethingwithconfig(context)方法,而此時(shí)context并沒有被初始化煌寇,就會(huì)導(dǎo)致程序出錯(cuò)焕蹄。
從上面可以看出,指令重排序不會(huì)影響單個(gè)線程的執(zhí)行唧席,但是會(huì)影響到線程并發(fā)執(zhí)行的正確性擦盾。
為什么要單獨(dú)講解原子性呢嘲驾?原子性在多線程編程中要注意什么問題?如何解決指令重排在多線程下的影響呢迹卢?請(qǐng)關(guān)注下篇文章辽故。