標籤:監視器 src alt 一個 共用變數 標記 size 緩衝 同步
如果一個線程對共用變數的修改,能夠被其它線程看到,那麼就能說明共用變數線上程之間是可見的。如果一個變數在多個線程的工作記憶體中都存在副本,那麼這個變數就是這幾個線程的共用變數。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變數的副本
Java記憶體可見度