Java多線程之內存可見性

一、JAVA內存模型簡介

JAVA Merory  Model描述了JAVA程序中各種變量(線程共享變量)的訪問規則,以及在JVM中將變量存儲到內存和從內存中讀取變量這樣的底層細節。

所有的變量都保存在主內存中,但是每個線程都有自己的獨立工作內存,保存該線程使用到的變量的一個副本。


兩條規定

1.線程對共享變量的操作只能在獨立的工作內存中進行,不能在主內存中直接讀寫;

2.不同線程之間無法直接訪問其他線程的工作內存中的變量,需要通過主內存完成。

實現共享變量的可見性,必須保證兩點

1.線程修改後的共享變量能及時從工作內存刷新到主內存中;

2.其他線程能及時把共享變量的最新值從主內存中更新到工作內存中。

JAVA從語言層面支持的可見性實現方式

synchronized; volatile


二、synchronized實現可見性


synchronized除了使代碼同時只能被一個線程執行的功能,還有一個功能就是實現可見性。

關於synchronized的兩條規定

1.線程解鎖前,必須把共享變量的最新值刷到主內存去;
2.線程加鎖時,清空工作內存的共享變量值,從主內存中重新讀取。 
這兩條規定保證了線程在解鎖前對共享變量的操作在下次加鎖前對其他線程可見。

synchronized實現可見性的過程


1.獲得互斥鎖
2.清空工作內存
3.從主內存中拷貝共享變量的最新值到工作內存
4.執行代碼
5.把修改後的變量值更新到主內存
6.釋放鎖

as-if-serial與重排序

重排序: 代碼的執行順序和書寫順序不相同,指令重排序是編譯器或處理器爲了提高性能而做的優化。
分爲 編譯器優化、處理器指令級並行重排序、內存系統的重排序(對讀寫緩存的優化,如上所說的那些)


as-if-serial:
無論如何重排序,程序執行的結果與代碼順序執行的結果相一致。(這並不是說就不進行重排序了!)

Java在單線程下是保證as-if-serial的。


導致共享變量在線程間不可見的原因及解決方案

1.線程交叉執行;  synchronized的原子性解決這個問題
2.重排序+線程交叉執行(甚至if() 與 {}中都可能被重排序,控制依賴關係並不是重排序的約束,只有數據依賴關係纔是) synchronized的原子性解決這個問題
3.共享變量更新後的值沒有在主內存與工作內存之間及時更新 synchronized的兩條規定解決這個問題


三、volatile實現可見性


volatile關鍵字可以保證volatile變量的可見性,但是不能保證volatile變量複合操作的原子性。

volatile如何實現

通過加入內存屏障和禁止指令重排序來實現:

對volatile變量執行寫操作時,會在寫操作後加一條store屏障指令,會把CPU寫緩存的緩存強制刷新到主內存,還能防止處理器把volatile前的變量重排序到volatile之後。
對volatile變量執行讀操作時,會在讀操作前加一條load屏障指令。在讀取時強制從主內存中讀取。

通俗的說,就是volatile變量每次被線程訪問時,都強制從主內存中重讀該變量的值,而不是從工作內存的緩存中;當該值發生變化時,強制線程把最新的值刷新到主內存中。

不能保證volatile變量複合操作的原子性

比如 :
private  volatile int a=0;
a++;   //不是原子操作:分三步, 讀取a的值,a+1;寫回a的值

但是下面的是原子操作:
synchronized(this){
a++;
}

除了synchronized之外,還可以用Lock:
Lock lock= new ReentrantLock();  //可重入鎖
然後在後面:
lock.lock();
try{
a++;
}finally{
lock.unlock();
}

volatile的適用場景

1.對變量的寫入操作不依賴當前值
   不滿足: num++; num=num*5 ...
   滿足:    布爾變量等等
2.該變量沒有包含在具有其他變量的不變式中
   即程序中有多個volatile變量時,各自要獨立。如a,b;  如果有a<b這樣的式子,就不滿足。

所以volatie的適用範圍不如synchronized大。

四、synchronized與volatile的比較


1.volatile不需要加鎖,比synchronized輕量級,不會造成線程阻塞。
2.synchronized既可以保證可見性,又可以保證原子性;而volatile只能保證可見性。



===========================================================================
(文章是看完慕課的視頻後寫的,感謝 http://www.imooc.com/view/352)

轉載請註明出處,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章