若是一個線程對共享變量的修改,可以被其它線程看到,那麼就能說明共享變量在線程之間是可見的。若是一個變量在多個線程的工做內存中都存在副本,那麼這個變量就是這幾個線程的共享變量。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變量的副本spa