再來重復下八大原則:
- 單線程happen-before原則:在同一個線程中,書寫在前面的操作happen-before后面的操作。
- 鎖的happen-before原則:同一個鎖的unlock操作happen-before此鎖的lock操作。
- volatile的happen-before原則:對一個volatile變量的寫操作happen-before對此變量的任意操作(當然也包括寫操作了)。
- happen-before的傳遞性原則:如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。
- 線程啟動的happen-before原則:同一個線程的start方法happen-before此線程的其它方法。
- 線程中斷的happen-before原則:對線程interrupt方法的調用happen-before被中斷線程的檢測到中斷發送的代碼。
- 線程終結的happen-before原則:線程中的所有操作都happen-before線程的終止檢測。
- 對象創建的happen-before原則:一個對象的初始化完成先于他的finalize方法調用。
首先在看本文前,最好先看下《java并發編程實戰》之java內存模型這篇文章,對java內存模型有個了解。
happen-before 在這里不能理解成在什么之前發生,它和時間沒有任何關系。個人感覺解釋成“生效可見于” 更準確。下面通過對這八個原則詳細解釋來加深對“生效可見于”的理解。
在同一個線程中,書寫在前面的操作happen-before后面的操作: 好多文章把這理解成書寫在前面先發生于書寫在后面的代碼,但是指令重排序,確實可以讓書寫在后面的代碼先于書寫在前面的代碼發生。這是里把happen-before 理解成“先于什么發生”,其實happen-beofre在這里沒有任何時間上的含義。比如下面的代碼:
?
int a = 3; //1
int b = a + 1; //2
這里 //2 對b賦值的操作會用到變量a,那么java的“單線程happen-before原則”就保證 //2的中的a的值一定是3,而不是0,5,等其他亂七八糟的值,因為//1 書寫在//2前面, //1對變量a的賦值操作對//2一定可見。因為//2 中有用到//1中的變量a,再加上java內存模型提供了“單線程happen-before原則”,所以java虛擬機不許可操作系統對//1 //2 操作進行指令重排序,即不可能有//2 在//1之前發生。但是對于下面的代碼:
?
int a = 3;
int b = 4;
兩個語句直接沒有依賴關系,所以指令重排序可能發生,即對b的賦值可能先于對a的賦值。
同一個鎖的unlock操作happen-beofre此鎖的lock操作: 話不多說直接看下面的代碼:
?
public class A {public int var;private static A a = new A();private A(){}public static A getInstance(){return a;}public synchronized void method1(){var = 3;}public synchronized void method2(){int b = var;}public void method3(){synchronized(new A()){ //注意這里和method1 method2 用的可不是同一個鎖哦var = 4;}}
}
?
//線程1執行的代碼:
A.getInstance().method1();
?
//線程2執行的代碼:
A.getInstance().method2();
?
//線程3執行的代碼:
A.getInstance().method3();
如果某個時刻執行完“線程1” 馬上執行“線程2”,因為“線程1”執行A類的method1方法后肯定要釋放鎖,“線程2”在執行A類的method2方法前要先拿到鎖,符合“鎖的happen-before原則”,那么在“線程2”method2方法中的變量var一定是3,所以變量b的值也一定是3。但是如果是“線程1”、“線程3”、“線程2”這個順序,那么最后“線程2”method2方法中的b值是3,還是4呢?其結果是可能是3,也可能是4。的確“線程3”在執行完method3方法后的確要unlock,然后“線程2”有個lock,但是這兩個線程用的不是同一個鎖,所以JMM這個兩個操作之間不符合八大happen-before中的任何一條,所以JMM不能保證“線程3”對var變量的修改對“線程2”一定可見,雖然“線程3”先于“線程2”發生。
對一個volatile變量的寫操作happen-before對此變量的任意操作:
?
volatile int a;
?
a = 1; //1
?
b = a; //2
如果線程1 執行//1,“線程2”執行了//2,并且“線程1”執行后,“線程2”再執行,那么符合“volatile的happen-before原則”所以“線程2”中的a值一定是1。
如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作:如果有如下代碼塊:
?
volatile int var;
int b;
int c;
?
b = 4; //1
var = 3; //2
?
c = var; //3
c = b; //4
假設“線程1”執行//1 //2這段代碼,“線程2”執行//3 //4這段代碼。如果某次的執行順序如下:
//1 //2 //3 //4。那么有如下推導( hd(a,b)表示a happen-before b):
因為有hd(//1,//2) 、hd(//3,//4) (單線程的happen-before原則)
且hd(//2,//3) (volatile的happen-before原則)
所以有 hd(//1,//3),可導出hd(//1,//4) (happen-before原則的傳遞性)
所以變量c的值最后為4
如果某次的執行順序如下:
//1 //3 //2// //4 那么最后4的結果就不能確定嘍。其原因是 //3 //2 直接符合上述八大原則中的任何一個,不能通過傳遞性推測出來什么。
通過對上面的四個原則的詳細解釋,省下的四個原則就比較顯而易見了。這里就不做詳細解釋了。歡迎積極留言大家一起討論。
作者:aworker
鏈接:https://www.jianshu.com/p/1508eedba54d
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。