Volatile 关键字 内存可见性
?
1、問題引入
實現線程:
public class ThreadDemo implements Runnable {private boolean flag = false;@Overridepublic void run(){flag = true;System.out.println("flag=" + flag);}public boolean isFlag(){return flag;}public void setFlag(boolean flag){this.flag = flag;} }測試類:
public class VolatileTest {public static void main(String[] args){ThreadDemo threadDemo = new ThreadDemo();new Thread(threadDemo).start();while (true){if(threadDemo.isFlag()){System.out.println("----------------");break;}}} }
結果:flag=true,并且程序不會停止
結果分析:從結果中看到,線程threadDemo 將 flag 被修改為 ture 了,但是 while 循環中的 if 判斷中沒有進入(即 flag = false);主線程中的flag和threadDemo 中的flag值不一樣。
內存可見性的問題:多個線程操作共享數據時,各個線程之間的數據不一致;
JVM會為每個線程分配一個獨立的緩存用于提高效率。
(1)程序開始運行時主存中的flag=false;
(2)接著線程threadDemo會修改flag數據:threadDemo先從主存中獲取到flag數據(false),然后在線程threadDemo中的緩存數據修改為true;
(3)在這個時候main線程來了,main線程從主存中讀取到的flag=false;
(4)線程threadDemo將flag=true寫入到主存中;
(5)while(true) 的執行效率特別高,以至于 main 線程沒有機會從主存中讀取數據;
解決內存可見性的問題:
1、增加同步鎖,用?synchronized 進行同步,代碼如下:
public class VolatileTest {public static void main(String[] args){ThreadDemo threadDemo = new ThreadDemo();new Thread(threadDemo).start();while (true){synchronized (threadDemo){if (threadDemo.isFlag()){System.out.println("----------------");break;}}}} }以上代碼存在的問題:增加同步鎖解決了內存可見性的問題,但是效率特別低;
2、增加 volatile 關鍵字,修飾線程的共享數據,代碼如下:
public class ThreadDemo implements Runnable {private volatile boolean flag = false;@Overridepublic void run(){flag = true;System.out.println("flag=" + flag);}public boolean isFlag(){return flag;}public void setFlag(boolean flag){this.flag = flag;} }?
?Volatile關鍵字不具備“互斥性”,Volatile不能保證“原子性”;
Java內存模型(Java Memory Model)描述了Java程序中各種變量(線程共享變量)的訪問規則,以及在JVM中將變量存儲到內存和從內存中讀取出變量這樣的底層細節。
??? 所有的變量都存儲在主內存中。每個線程都有自己獨立的工作內存,里面保存該線程使用到的變量的副本(主內存中該變量的一份拷貝),如圖
兩條規定:
- 線程對共享變量的所有操作都必須在自己的工作內存中進行,不能直接從主內存中讀取
- 不同線程之間無法直接訪問其他線程工作內存中的變量,線程間變量值的傳遞需要通過主內存來完成。
在這種模型下會存在一個現象,即緩存中的數據與主內存的數據并不是實時同步的,各CPU(或CPU核心)間緩存的數據也不是實時同步的。這導致在同一個時間點,各CPU所看到同一內存地址的數據的值可能是不一致。
?
volatile關鍵字:
能夠保證volatile變量的可見性,不能保證volatile變量復合操作的原子
volatile如何實現內存的可見性:
深入來說:通過加入內存屏障和禁止重排序優化來實現的
在每個volatile寫操作前插入StoreStore屏障,在寫操作后插入StoreLoad屏障;
在每個volatile讀操作前插入LoadLoad屏障,在讀操作后插入LoadStore屏障;
通俗地講:volatile變量在每次被線程訪問時,都強迫從主內存中重讀該變量的值,而當該變量發生變化時,又會強迫將最新的值刷新到主內存。這樣任何時刻,不同的線程總能看到該變量的最新值。
線程寫volatile變量的過程:
(1)改變線程工作內存中volatile變量副本的值
(2)將改變后的副本的值從工作內存刷新到主內存
線程讀volatile變量的過程:
(2)從主內存中讀取volatile變量的最新值到線程的工作內存中
(2)從工作內存中讀取volatile變量的副本
?
?參考鏈接:
https://www.cnblogs.com/amei0/p/8378625.html
?
轉載于:https://www.cnblogs.com/yufeng218/p/10116595.html
總結
以上是生活随笔為你收集整理的Volatile 关键字 内存可见性的全部內容,希望文章能夠幫你解決所遇到的問題。