1. 什么是先行發(fā)生原則(happens-before)
先行發(fā)生是Java內(nèi)存模型中定義的兩項(xiàng)操作之間的偏序關(guān)系挂签,如果說(shuō)操作A先行發(fā)生于操作B纬霞,就是說(shuō)A產(chǎn)生的影響能被B觀察到撵术,”影響“包括修改了內(nèi)存中的共享變量值、發(fā)送了消息、調(diào)用了方法等蠢挡。
例如:
// 線程A中執(zhí)行
i = 1;
// 線程B中執(zhí)行
j = i;
// 線程C中執(zhí)行
i = 2;
如果說(shuō)線程A是先行發(fā)生于線程B的,那么可以確定在線程B執(zhí)行之后 j=1
凳忙,因?yàn)楦鶕?jù)先行發(fā)生原則业踏,A操作 i = 1
的結(jié)果可以被B觀察到,并且線程C還沒(méi)有執(zhí)行涧卵。
那么如果線程C是在A與B之間勤家,j
的值是多少呢?答案是不確定柳恐。
2. 自動(dòng)實(shí)現(xiàn)先行發(fā)生的規(guī)則
以下是Java內(nèi)存模型中天然的先行發(fā)生規(guī)則伐脖,對(duì)于不在此列的關(guān)系,就沒(méi)有順序性保障乐设,虛擬機(jī)可以隨意的進(jìn)行重排:
- 程序次序規(guī)則:代碼執(zhí)行順序符合流程控制順序讼庇。
- 管程鎖定規(guī)則:unlock 操作先行發(fā)生于后面對(duì)同一個(gè)鎖的 lock 操作。
- volatile 變量規(guī)則:對(duì)一個(gè) volatile 變量的寫操作先行發(fā)生于后面對(duì)這個(gè)變量的讀操作近尚。
- 線程啟動(dòng)規(guī)則:線程對(duì)象 start() 方法先行發(fā)生于此線程的每一個(gè)動(dòng)作蠕啄。
- 線程終止規(guī)則:線程中所有操作先行發(fā)生于對(duì)此線程的終止檢測(cè)。
- 線程中斷規(guī)則:對(duì)線程 interrupt() 方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測(cè)到中斷事件的發(fā)送:可以通過(guò) Thread.interrupted() 方法檢測(cè)到是否有中斷發(fā)生肿男。
- 對(duì)象終結(jié)規(guī)則:一個(gè)對(duì)象的初始化完成先行發(fā)生于它的 finalize() 方法的開始介汹。
- 傳遞性:如果操作A先行發(fā)生于操作B,B先行發(fā)生于C舶沛,那么A先行發(fā)生于C嘹承。
3. 示例
private int value = 0;
public void setValue(int value) {
this.value = value;
}
public int getValue(){
return value;
}
假設(shè)有2個(gè)線程 A 和 B,A 先調(diào)用了 setValue(1)
如庭,然后 B 調(diào)用 get 方法叹卷,那么 B 的返回值是什么?
我們對(duì)照一下上面的那些原則:
- 2個(gè)方法分別在2個(gè)線程中調(diào)用坪它,不在一個(gè)線程中骤竹,”程序次序規(guī)則“不適用;
- 沒(méi)有同步塊往毡,不會(huì)發(fā)生 lock 和 unlock 操作蒙揣,”管程鎖定規(guī)則“不適用;
- value 沒(méi)有使用 volidate 關(guān)鍵字开瞭,”volatile 變量規(guī)則“不適用懒震;
- 其他的線程罩息、對(duì)象的啟動(dòng)終結(jié)之類的規(guī)則和此代碼沒(méi)有關(guān)系,都不適用个扰;
所以瓷炮,B 的返回值無(wú)法確定,就是說(shuō)線程不安全递宅。