Happens-Before 並不是說前面一個操作發生在後續操作的前面, 它真正要表達的是: 前面一個操作的結果對後續操作是可見的。
一、理解happens-before
1、如果⼀個操作happens-before另⼀個操作,那麼第⼀個操作的執⾏結果將對第⼆個操作可⻅,⽽且第⼀個操作的執⾏順序排在第⼆個操作之前。
2、兩個操作之間存在happens-before關係,並不意味着Java平臺的具體實現必須要按照happens-before關係指定的順序來執⾏。如果重排序之後的執⾏結果,與按happens-before關係來執⾏的結果⼀致,那麼JMM也允許這樣的重排序。
如果操作A happens-before操作B,那麼操作A在內存上所做的操作對操作B都是可⻅的,不管它們在不在⼀個線程
happens-before關係本質上和as-if-serial語義是⼀回事。as-if-serial語義保證單線程內重排序後的執⾏結果和程序代碼本身應有的結果是⼀致的,happens-before關係保證正確同步的多線程程序的執⾏結果不被重排序改變
二、happens-before規則
下邊前三個規則,都可以看這個例子來理解
class VolatileExample{
int a=0;
volatile boolean flag=false;
public void writer(){
a=1; // 操作1
flag=true; // 操作2
}
public void reader(){
if(flag){ // 操作3
int i=a; // 操作4
//這裏i會是多少呢?
}
}
}
1、程序順序規則
⼀個線程中的每⼀個操作,happens-before於該線程中的任意後續操作。
程序前面對某個變量的修改一定是對後續操作可見的。
例如上邊實例代碼,按照程序順序規則,1 happens-before 2;3 happens-before 4
2、volatile變量規則
對⼀個volatile域的寫,happens-before於任意後續對這個volatile域的讀。
例如上邊實例代碼,按照volatile變量規則,2 happens-before 3
3、傳遞性規則
如果A happens-before B,且B happens-before C,那麼A happensbefore C。
例如上邊實例代碼,按照傳遞性規則,1 happens-before 4
4、管程中鎖的規則
對⼀個鎖的解鎖,happens-before於隨後對這個鎖的加鎖。
管程是一種通用的同步原語, 在Java中指的就是synchronized, synchronized是Java裏對管程的實現。
synchronized (this) { //此處自動加鎖
// x是共享變量,初始值=10
if (this.x < 12) {
this.x = 12;
}
} //此處自動解鎖
根據管程中鎖的規則,線程A執行完代碼塊後x的值會變成12(執行完自動釋放鎖) , 線程B進入代碼塊時, 能夠看到線程A對x的寫操作,也就是線程B能夠看到x==12。
5、線程start規則
主線程A啓動子線程B後, 子線程B能夠看到主線程在啓動子線程B前的操作(指的是對共享變量的操作)。
如果線程A執⾏操作ThreadB.start()啓動線程B,那麼A線程的ThreadB.start()操作happens-before於線程B中的任意操作
// a是共享變量
Thread A = new Thread(() -> {
Thread B = new Thread(() -> {
System.out.println(a); // 這裏a爲1 // 操作2
});
a = 1; // 操作1
B.start();
});
A.start();
根據線程start規則,1 happens-before 2,線程A對共享變量a=1的操作對於線程B是可見的
6、線程join規則
主線程A等待子線程B完成, 當子線程B完成後 ,主線程能夠看到子線程的操作(指的是對共享變量的操作)。
如果在線程A中, 調用線程B的 join() 併成功返回, 那麼線程B中的任意操作Happens-Before 於該 join() 操作的返回
// a是共享變量
Thread A = new Thread(() -> {
Thread B = new Thread(() -> {
a=2; // 操作1
});
a = 1;
B.start();
try {
B.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
int i = a; // 操作2
System.out.println(i); // 這裏i的值爲2
});
A.start();
根據線程join規則,1 happens-before 2,線程B對共享變量的修改,在線程A調用B.join()之後皆可見。
關於start與join,start後,A的操作對B可見,join後,B的操作對A可見。