本文搬運自<<極客時間>>哈~相當于做個筆記了糕档。。痊硕。
為什么會有happens-before 規(guī)則赊级?
因為jvm會對代碼進行編譯優(yōu)化,指令會出現重排序的情況岔绸,為了避免編譯優(yōu)化對并發(fā)編程安全性的影響理逊,需要happens-before規(guī)則定義一些禁止編譯優(yōu)化的場景,保證并發(fā)編程的正確性亭螟。
public class VolatileExample {
int x = 0 ;
volatile boolean v = false;
public void writer(){
x = 42;
v = true;
}
public void reader(){
if (v == true){
// 這里x會是多少呢
}
}
}
拋出問題:假設有兩個線程A和B挡鞍,A執(zhí)行了writer方法,B執(zhí)行reader方法预烙,那么B線程中獨到的變量x的值會是多少呢墨微?
jdk1.5之前,線程B讀到的變量x的值可能是0扁掸,也可能是42翘县,jdk1.5之后,變量x的值就是42了谴分。原因是jdk1.5中锈麸,對volatile的語義進行了增強。來看一下happens-before規(guī)則在這段代碼中的體現牺蹄。
1. 規(guī)則一:程序的順序性規(guī)則
一個線程中忘伞,按照程序的順序,前面的操作happens-before后續(xù)的任何操作。
對于這一點氓奈,可能會有疑問翘魄。順序性是指,我們可以按照順序推演程序的執(zhí)行結果舀奶,但是編譯器未必一定會按照這個順序編譯暑竟,但是編譯器保證結果一定==順序推演的結果。
2. 規(guī)則二:volatile規(guī)則
對一個volatile變量的寫操作育勺,happens-before后續(xù)對這個變量的讀操作但荤。
3. 規(guī)則三:傳遞性規(guī)則
如果A happens-before B,B happens-before C涧至,那么A happens-before C腹躁。
jdk1.5的增強就體現在這里』瑁回到上面例子中潜慎,線程A中,根據規(guī)則一蓖康,對變量x的寫操作是happens-before對變量v的寫操作的铐炫,根據規(guī)則二,對變量v的寫操作是happens-before對變量v的讀操作的蒜焊,最后根據規(guī)則三倒信,也就是說,線程A對變量x的寫操作泳梆,一定happens-before線程B對v的讀操作鳖悠,那么線程B在注釋處讀到的變量x的值,一定是42.
4.規(guī)則四:管程中的鎖規(guī)則
對一個鎖的解鎖操作优妙,happens-before后續(xù)對這個鎖的加鎖操作乘综。
這一點不難理解。
5.規(guī)則五:線程start()規(guī)則
主線程A啟動線程B套硼,線程B中可以看到主線程啟動B之前的操作卡辰。也就是start() happens before 線程B中的操作。
6.規(guī)則六:線程join()規(guī)則
主線程A等待子線程B完成邪意,當子線程B執(zhí)行完畢后九妈,主線程A可以看到線程B的所有操作。也就是說雾鬼,子線程B中的任意操作萌朱,happens-before join()的返回。