基本概念
1.可見性
當一個線程修改了共享變量時,另一個線程可以讀取到這個修改後的值。
2.記憶體屏障(memory barriers)
處理器的一組指令,用于實作對記憶體操作的順序限制。
3.緩沖行
cpu告訴緩存中可以配置設定的最小存儲機關,處理器填寫緩存行時,會加載整個緩存行。
4.lock字首的指令
lock字首的指令在多核處理器下會發生兩件事情:
1)将目前處理器的緩存行的資料協會到系統記憶體。
2)這個寫回記憶體的操作會使其他cpu緩存了該記憶體的位址的資料無效。
5.緩存一緻性協定
在多處理器下,為零保證各個處理器的緩存是一緻的,每個處理器都會通過嗅探在總線上傳播的資料來檢查自己緩存的值是不是過期了。當處理器發現自己緩存行對應的位址被修改,就會将目前處理器的緩存行設定為無效狀态。當處理器對這個資料進行讀寫的時候,會重新把資料從記憶體中讀取到處理器緩存中。
6.cas
compareandswap 比較并交換
cas操作需要輸入兩個值,一個舊值(執行cas操作前的值,期望值)和一個新值,隻有當目前值等于舊值時,才可以将目前值設定為新值,否則不設定。這是一個原子操作,由硬體保證。
7.重排序規則
從根本上來所,jmm 對編譯器和處理器的重排序限制隻有一條,隻要不改變程式執行的結果(指的是單線程或者正确同步的多線程環境下),那麼編譯器和處理器怎麼優化都可以。
volatile
從上面的lock字首指令和緩存一緻性協定可以看出來,這就是volatile的實作原理了。
實際上,valatile變量被寫入時,确實加了一個lock字首的指定,以此來達到可見性的目的。
final
final域隻能被顯示地指派一次,但是這并不代表final域不能被多次初始化。
比如:final int i ;i在構造函數中被指派之前,就會被初始化為預設的值:0.通過調試代碼可以證明這一點。
為了保證final域的值不會在為初始化的情況下被通路到,程式員隻需要保證一點即可:即,在構造函數中,正在被構造的對象(this)沒有“逸出”,那麼不需要任何同步手段,就能保證任意線程看到的final域,包括基本類型和引用類型,都是已經被正确地通過構造函數初始化過了的。
一個會是正在被構造的對象逸出的例子:
public class finaltest{
final int i;
static finaltest obj;
public finaltest(){
i =1;
/**
*這裡會使正在被構造的對象逸出,如果和上一句做了重排序,那麼其他線程就可以通過obj通路到還為被初始化的final域。
**/
obj = this;
}
}
happens-before規則
happens-before的含義
happen-before規則用來描述兩個操作之間的順序關系,這兩個操作可以再一個線程内,也可以不再一個線程内。此順序并不嚴格意味着執行時間上的順序,而是至前一個操作的結果要對後一個操作可見。
happens-before關系的定義如下:
如果一個happens-before另一個操作,那麼第一個操作的執行結果對第二個操作可見,而且第一個操作的執行順序排在第二個操作之前
兩個操作之間存在happens-before關系,并不意味着java平台的具體實作必須按照happens-before關系指定的順序來執行。如果重排序之後的執行結果,與按照happens-before關系來執行的結果一緻,那麼這種重排序并不非法。
舉例來說,如果在程式執行順序上,a先于b,并且a修改了共享變量,而b正好使用該共享變量,那麼a需要happen-before b,再直白一點,就是a對共享變量的修改,需要在b執行時,對b可見。
程式順序規則:一個線程中的每個操作,happens-before于該線程中的任意後續操作。
螢幕鎖規則:對一個鎖的解鎖,happens-before于随後對這個鎖的加鎖。
volatile規則:對一個volatile域的寫,happens-before于任意後續對這個volatile域的讀。
傳遞性:如果ahappens-before b,并且b happens-before c,那麼a happens-before c。
start()規則:如果線程a執行操作threadb.start(),那麼a線程的threadb.start()操作happens-before于線程b中的任意操作。
join()規則:如果線程a執行操作threadb.join()并成功傳回,那麼線程b的任意操作happens-before于線程a從threadb.join()操作成功傳回。
對所有這些規則的說明:ahappens-before b并不意味着a一定要先在b之前發生,而是說,如果a已經發生在了b前面,那麼a的操作結果一定要對b可見
作者:佚名
來源:51cto