再來重復下八大原則:
- 單線程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被中斷線程的檢測到中斷發(fā)送的代碼藻雪。
- 線程終結的happen-before原則:線程中的所有操作都happen-before線程的終止檢測证杭。
- 對象創(chuàng)建的happen-before原則:一個對象的初始化完成先于他的finalize方法調用扣孟。
首先在看本文前,最好先看下《java并發(fā)編程實戰(zhàn)》之java內存模型這篇文章粗蔚,對java內存模型有個了解蹋偏。
happen-before 在這里不能理解成在什么之前發(fā)生便斥,它和時間沒有任何關系。個人感覺解釋成“生效可見于” 更準確威始。下面通過對這八個原則詳細解釋來加深對“生效可見于”的理解枢纠。
在同一個線程中,書寫在前面的操作happen-before后面的操作: 好多文章把這理解成書寫在前面先發(fā)生于書寫在后面的代碼黎棠,但是指令重排序晋渺,確實可以讓書寫在后面的代碼先于書寫在前面的代碼發(fā)生。這是里把happen-before 理解成“先于什么發(fā)生”脓斩,其實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虛擬機不許可操作系統(tǒng)對//1 //2 操作進行指令重排序扛门,即不可能有//2 在//1之前發(fā)生。但是對于下面的代碼:
int a = 3;
int b = 4;
兩個語句直接沒有依賴關系纵寝,所以指令重排序可能發(fā)生论寨,即對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執(zhí)行的代碼:
A.getInstance().method1();
//線程2執(zhí)行的代碼:
A.getInstance().method2();
//線程3執(zhí)行的代碼:
A.getInstance().method3();
如果某個時刻執(zhí)行完“線程1” 馬上執(zhí)行“線程2”爽茴,因為“線程1”執(zhí)行A類的method1方法后肯定要釋放鎖葬凳,“線程2”在執(zhí)行A類的method2方法前要先拿到鎖,符合“鎖的happen-before原則”室奏,那么在“線程2”method2方法中的變量var一定是3火焰,所以變量b的值也一定是3。但是如果是“線程1”胧沫、“線程3”昌简、“線程2”這個順序占业,那么最后“線程2”method2方法中的b值是3,還是4呢纯赎?其結果是可能是3谦疾,也可能是4。的確“線程3”在執(zhí)行完method3方法后的確要unlock犬金,然后“線程2”有個lock念恍,但是這兩個線程用的不是同一個鎖,所以JMM這個兩個操作之間不符合八大happen-before中的任何一條晚顷,所以JMM不能保證“線程3”對var變量的修改對“線程2”一定可見峰伙,雖然“線程3”先于“線程2”發(fā)生。
對一個volatile變量的寫操作happen-before對此變量的任意操作:
volatile int a;
a = 1; //1
b = a; //2
如果線程1 執(zhí)行//1该默,“線程2”執(zhí)行了//2,并且“線程1”執(zhí)行后,“線程2”再執(zhí)行,那么符合“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”執(zhí)行//1 //2這段代碼,“線程2”執(zhí)行//3 //4這段代碼顿膨。如果某次的執(zhí)行順序如下:
//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
如果某次的執(zhí)行順序如下:
//1 //3 //2// //4 那么最后4的結果就不能確定嘍恋沃。其原因是 //3 //2 直接符合上述八大原則中的任何一個,不能通過傳遞性推測出來什么必指。
通過對上面的四個原則的詳細解釋囊咏,省下的四個原則就比較顯而易見了。這里就不做詳細解釋了塔橡。歡迎積極留言大家一起討論梅割。