那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

volatile的原理

科技綠洲 ? 來源:Java技術指北 ? 作者:Java技術指北 ? 2023-10-10 16:33 ? 次閱讀

今天來了解一下面試題:你對 volatile 了解多少。要了解 volatile 關鍵字,就得從 Java 內存模型開始。最后到 volatile 的原理。

一、Java 內存模型 (JMM)

大家都知道 Java 程序可以做到一次編寫然后到處運行。這個功勞要歸功于 Java 虛擬機。Java 虛擬機中定義了一種 Jva 內存模型(JMM),用來屏蔽掉各種硬件和操作系統之間內存訪問差異,讓 Java 程序可以在各個平臺中訪問變量達到相同的效果。

JMM 的主要目標是定義了程序中變量的訪問規則,就是內存中存放和讀取變量的一些底層的細節。

JMM 規則

  1. 變量包含實例字段,靜態字段,構成數組對象的元素,不包含局部變量和方法參數
  2. 變量都存儲在主內存上。
  3. 每個線程在 CPU 中都有自己的 工作內存 ,工作內存保存了被該線程使用到的變量的主內存副本拷貝。
  4. 線程對變量的所有操作都只能在工作內存,不能直接讀寫主內存的變量。
  5. 不同線程之間無法之間訪問對方工作內存中的變量。

圖片

定義一個靜態變量: static int a = 1;

線程 1 工作內存指向主內存操作
----a = 1--
a = 1<--a = 1線程 1 拷貝主內存變量副本
a = 3--a = 1線程 1 修改工作內存變量值
a = 3-->a = 3線程 1 工作內存變量存儲到主內存變量

上面的一系列內存操作,在 JMM 中定義了 8 種操作來完成。

JMM 交互

主內存和工作內存之間的交互,JMM 定義了 8 種操作來完成,每個操作都是原子性的。

  1. lock (鎖定): 作用于主內存變量,把一個變量標識為一條內存獨占的狀態。
  2. unlock (解鎖): 作用于主內存變量,把 lock 狀態的變量釋放出來,釋放出來后才能被其他線程鎖定。
  3. read (讀取): 作用于主內存變量,把一個變量的值從主內存傳輸到工作內存中。
  4. load (載入): 作用于工作內存變量,把 read 操作的變量放入到工作內存副本中。
  5. use (使用): 作用于工作內存變量,把工作內存中的變量的值傳遞給執行引擎,每當虛擬機遇到需要這個變量的值的字節碼指令時都執行這個操作。
  6. assgin (賦值): 作用于工作內存變量,把從執行引擎收到的值賦值給工作內存變量,每當虛擬機遇到需要賦值變量的值的字節碼指令時都執行這個操作。
  7. store (存儲): 作用于工作內存變量,把工作內存中的一個變量值,傳送到主內存。
  8. write (寫入): 作用于主內存變量,把 store 操作的從工作內存取到的變量寫入主內存變量中。

圖片

從上圖中可知,JMM 交互在一條線程中是不會出現任何的問題。但是當有兩條線程的時候,線程 1 已經修改了變量的值,但是并未刷新到主內存時,如果此時線程 2 讀取變量得到的值并不是線程 1 修改過的數據。

當引入線程 2 的時候 定義一個靜態變量: static int a = 1;

操作順序線程 1 工作內存線程 2 工作內存指向主內存操作
--------a = 1--
1a = 1--<--a = 1線程 1 拷貝主內存變量副本
2a = 3----a = 1線程 1 修改工作內存變量值
3a = 3---->a = 1線程 1 工作內存變量存儲到主內存變量,主內存變量還未更新
4.1a = 3a = 1<--a = 3線程 2 拷貝主內存變量副本隨后主內存變量更新線程 1 工作內存變量
4.2a = 3a = 1<--a = 3線程 1 工作內存變量存儲到主內存變量隨后線程 2 獲取主內存變量副本

下面就可以用 volatile 關鍵字解決問題。

二、volatile

volatile 可以保證變量對所有線程可見,一條線程修改的值,其他線程對新值可以立即得知。還可以禁止指令的重排序。

可見性

修改內存變量后立刻同步到主內存中,其他的線程立刻得知得益于 Java 的先行發生原則

先行發生原則中的 volatile 原則:一個 volatile 變量的寫操作先行于后面發生的這個變量的讀操作

定義一個靜態變量: static int a = 1;

線程 1 工作內存線程 2 工作內存指向主內存操作
------a = 1--
a = 1--<--a = 1線程 1 拷貝主內存變量副本
a = 3----a = 1線程 1 修改工作內存變量值
a = 3---->a = 1線程 1 工作內存變量存儲到主內存變量
a = 3a = 3<--a = 3volatile 原則: 主內存變量保存線程A工作內存變量操作在線程 2 工作內存讀取主內存變量操作之前

可見性原理

對 volatile 修飾的變量,在執行寫操作的時候會多出一條 lock 前綴的指令。JVM 將 lock 前綴指令發送給 CPU ,CPU 處理寫操作后將最后的值立刻寫回主內存,因為有 MESI 緩存一致性協議保證了各個 CPU 的緩存是一致的,所以各個 CPU 緩存都會對總線進行嗅探,本地緩存中的數據是否被別的線程修改了。

如果別的線程修改了共享變量的數據,那么 CPU 就會將本地緩存的變量數據過期掉,然后這個 CPU 上執行的線程在讀取共享變量的時候,就會從主內存重新加載最新的數據。

原子性

volatile 并不保證變量具有原子性。

public class VolatileTest implements Runnable {

    public static volatile int num;

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            num++;
        }

    }

    public static void main(String[] args) {
        for(int i = 0; i < 100; i++) {
            VolatileTest t = new VolatileTest();
            Thread t0 = new Thread(t);
            t0.start();
        }
        System.out.println(num);
        
    }
}

這段代碼的結果有可能不是 100000,有可能小于 100000。因為 num++ 并不是原子性的。

有序性

volatile 是通過禁止指令重排序來保證有序性。為了優化程序的執行效率 JVM 在編譯 Java 代碼的時候或者 CPU 在執行 JVM 字節碼的時候,不影響最終結果的前提下會對指令進行重新排序。

編譯器會根據以下策略將內存屏障插入到指令中,禁止重排序:

  1. 在 volatile 寫操作之前插入 StoreStore 屏障。禁止和 StoreStore 屏障之前的普通寫操作不會進行重排序。
  2. 在 volatile 寫操作之后插入 StoreLoad 屏障。禁止和 StoreLoad 屏障之后的 volatile 讀寫重排序。
  3. 在 volatile 讀操作之后插入 LoadLoad 屏障。禁止和 LoadLoad 之后的普通讀和 volatile 讀重排序。
  4. 在 volatile 寫操作之后插入 LoadStore 屏障。禁止和 LoadStore 屏障之后的普通寫操作重排序。
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 內存
    +關注

    關注

    8

    文章

    3055

    瀏覽量

    74327
  • JAVA
    +關注

    關注

    19

    文章

    2974

    瀏覽量

    105136
  • 模型
    +關注

    關注

    1

    文章

    3305

    瀏覽量

    49217
  • volatile
    +關注

    關注

    0

    文章

    45

    瀏覽量

    13060
收藏 人收藏

    評論

    相關推薦

    什么是volatile

    00. 目錄文章目錄00. 目錄01. volatile概述02. volatile應用場景03. volatile應用示例04. 嵌入式系統中應用05. volatile官方說明
    發表于 10-28 09:23

    c語言volatile的作用

    volatile,則編譯器會逐一地進行編譯并產生相應的機器代碼(產生四條代碼)。volatile變量有兩個作用:一個是告訴編譯器不要進行優化;另一個是告訴系統始終從內存中取變量的地址,而不是從緩存中取變量的值(加volatile
    發表于 11-03 09:13 ?2427次閱讀
    c語言<b class='flag-5'>volatile</b>的作用

    Volatile與多線程的認識與理解

    volatile是一個類型修飾符(type specifier),就像大家更熟悉的const一樣,它是被設計用來修飾被不同線程訪問和修改的變量。volatile的作用是作為指令關鍵字,確保本條指令
    發表于 12-01 10:31 ?1670次閱讀

    java之用volatile和不用volatile的區別

    volatile是一個類型修飾符(type specifier),就像大家更熟悉的const一樣,它是被設計用來修飾被不同線程訪問和修改的變量。Java具有簡單性、面向對象、分布式、健壯性、安全性、平臺獨立與可移植性、多線程、動態性等特點。
    發表于 12-01 10:52 ?3496次閱讀

    volatile修飾的變量的認識和理解

     談到volatile,理解原子性和易變性是不同的概念這一點很重要,volatile是輕量級的鎖,它只具備可見性,但沒有原子特性。如果你將一個域聲明為volatile,那么只要對這個域產生了寫操作
    發表于 12-01 11:36 ?5774次閱讀
    <b class='flag-5'>volatile</b>修飾的變量的認識和理解

    Java中volatile的作用以及用法

    Java 語言中的 volatile 變量可以被看作是一種 “程度較輕的 synchronized”;與 synchronized 塊相比,volatile 變量所需的編碼較少,并且運行時開銷也較少,但是它所能實現的功能也僅是 synchronized 的一部分。
    發表于 12-01 12:14 ?7026次閱讀

    volatile變量定義的意義和該用在哪里

    volatile 影響編譯器編譯的結果,volatile指出 變量是隨時可能發生變化的,與volatile變量有關的運算,不要進行編譯優化,以免出錯
    發表于 03-07 15:29 ?3714次閱讀
    <b class='flag-5'>volatile</b>變量定義的意義和該用在哪里

    C語言類型修飾符Volatile的使用說明

    C語言是我們經常需要用到的語言,C語言中的類型修飾符Volatile大家知道怎么使用嗎? volatile是一個類型修飾符(type specifier).volatile的作用是作為指令關鍵字
    的頭像 發表于 09-19 10:54 ?3587次閱讀

    volatile有哪些使用誤區

    在建立編譯環境的時候用typedef定義了指向volatile 單元的指針,最后終于發現行不通。
    發表于 08-06 17:34 ?0次下載
    <b class='flag-5'>volatile</b>有哪些使用誤區

    如何使用C++語法中的volatile

    volatile volatile int i = 10; volatile 關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素(操作系統、硬件、其它線程等)更改。所以
    的頭像 發表于 09-09 09:38 ?1516次閱讀

    C++基礎語法之volatile、assert()和sizeof()

    volatile volatile int i = 10; volatile 關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素(操作系統、硬件、其它線程等)更改。所以
    的頭像 發表于 09-09 09:48 ?1337次閱讀

    【嵌入式】C語言中volatile關鍵字

    00. 目錄文章目錄00. 目錄01. volatile概述02. volatile應用場景03. volatile應用示例04. 嵌入式系統中應用05. volatile官方說明
    發表于 10-21 10:21 ?6次下載
    【嵌入式】C語言中<b class='flag-5'>volatile</b>關鍵字

    C語言中的volatile是什么

    學C語言時有一個奇怪的關鍵字volatile,這到底有什么用呢?
    的頭像 發表于 02-17 14:29 ?1267次閱讀
    C語言中的<b class='flag-5'>volatile</b>是什么

    volatile的實現原理分析

    `volatile`是一個輕量級的`synchronized`,一般作用于 **變量** ,在多處理器開發的過程中保證了內存的可見性。相比于`synchronized`關鍵字,`volatile`關鍵字的執行成本更低,效率更高
    的頭像 發表于 05-11 17:33 ?680次閱讀
    <b class='flag-5'>volatile</b>的實現原理分析

    介紹下volatile的底層原理

    線程安全的三大特性,原子性、可見性、有序性,這三大特性與我們之前整理的內容息息相關。本篇重點介紹下volatile的底層原理,幫助我們更好的理解java并發包。
    的頭像 發表于 06-09 16:17 ?911次閱讀
    介紹下<b class='flag-5'>volatile</b>的底層原理
    凯时百家乐官网技巧| 旅百家乐官网赢钱律| 威尼斯人娱乐城赌博| 澳门百家乐官网网上| 卢克索百家乐的玩法技巧和规则| 百家乐官网的玩法和技巧| 巨星百家乐的玩法技巧和规则 | 噢门百家乐官网玩的技巧| 金木棉百家乐网络破解| 百家乐官网稳中一注法| 新锦江百家乐的玩法技巧和规则| 百家乐官网智能软件| 大发888易付168| 红树林百家乐官网的玩法技巧和规则 | 新和县| 太阳城百家乐出千技术| 百家乐官网是多少个庄闲| 免费百家乐过滤软件| 嘉年华百家乐官网的玩法技巧和规则 | 金殿百家乐的玩法技巧和规则 | 大发888娱乐场开户注册| 免费百家乐官网统计软件| 惠州市| 澳门百家乐手机软件| 百家乐官网官网站| 恒和国际| 在线百家乐电脑| 网上百家乐官网游戏下载| 六合彩今天开什么| 百家乐官网赌钱| 威尼斯人娱乐城博彩| 星期八百家乐官网的玩法技巧和规则 | 百家乐官网投注技巧公式| 德州扑克锦标赛| 百家乐知识技巧玩法| 百家乐官网赌场怎么玩| 利来| 百家乐资深 | 维也纳娱乐城| 鸟巢百家乐的玩法技巧和规则| 宕昌县|