java內存之可見性volatile

如果一個線程對共享變量的修改,能夠被其它線程看到,那麼就能說明共享變量在線程之間是可見的。如果一個變量在多個線程的工作內存中都存在副本,那麼這個變量就是這幾個線程的共享變量。Java內存模型(Java Memory Model,JMM)描述了Java程序中各種變量(線程共享變量)的訪問規則,以及在JVM中將變量存儲到內存和從內存中讀取出變量這樣的底層細節。所有的變量都儲存在主內存中。每個線程都有自己獨立的工作內存,裏面保存了該線程使用到的變量的副本(主內存中該變量的一份拷貝),如下圖所示。

爲什麼會出現共享變量可見性的問題,這是因爲線程對共享變量的所有操作都必須在自己的工作內存中進行,不能從主內存中讀寫;而且不同線程之間無法直接訪問其它線程工作內存中的變量,線程間變量值的傳遞需要通過主內存來完成。線程1對共享變量的修改要想被線程2及時看到,必須要經過如下兩個步驟:
1. 把工作內存1中更新過的共享變量刷新到主內存中;
2. 把內存中最新的共享變量的值更新到工作內存2中
Java語言層面支持的可見性實現方式有兩種:
1. synchronized
2. volatile

synchronized不僅能通過互斥鎖來實現同步,而且還能夠實現可見性。Java內存模型關於Synchronized有兩條規定:
* 線程釋放鎖之前,JMM會將工作內存中的共享變量刷新到主內存中;
* 線程加鎖時,將清空工作內存中共享變量的值,從而使用共享變量時需要從主內存中重新讀取最新的值
線程執行互斥代碼的過程:
1. 獲取監視器鎖
2. 清空工作內存
3. 從主內存中拷貝變量的最新副本到工作內存
4. 執行代碼
5. 將更改後的共享變量的值刷新到主內存
6. 釋放監視器鎖
如果某個任務處於一個對標記爲synchronized的方法的調用中,那麼在這個線程從該方法返回之前,其它所有要調用類中任何標記爲synchronized方法的線程都會被阻塞。
volatile通過加入內存屏障和禁止指令重排序優化來實現的:
* 對volatile變量執行寫操作時,會在寫操作後加入一條store屏障指令,這樣就會把讀寫時的數據緩存加載到主內存中;
* 對volatile變量執行讀操作時,會在讀操作前加入一條load屏障指令,這樣就會從主內存中加載變量;
所以說,volatile變量在每次被線程訪問時,都強迫從主內存中重讀該變量的值,而當該變量發生變化時,就會強迫線程將最新的值刷新到主內存,這樣任何時刻,不同的線程總能看到該變量的最新值。
線程寫volatile變量的過程:
1. 改變線程工作內存中volatile變量副本的值;
2. 將改變後的副本的值從工作內存刷新到主內存中
線程讀volatile變量的過程:
1. 從主內存中讀取volatile變量的最新值到線程的工作內存中;
2. 從工作內存中讀取volatile變量的副本

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