Java研發技術——Volatile原理詳解

2020-12-04 咔咔侃技術

volatile

volatile 只能保證對單次讀/寫的原子性。i++ 這種符合操作操作不能保證原子性。禁止指令重排可見性volatile讀的內存語義

當讀一個volatile變量時,JMM會把該線程對應的本地內存置為無效。線程接下來將從主內存中讀取共享變量(注意不僅僅是一個volatile變量,是所有共享變量)

volatile寫的內存語義

當寫一個volatile變量時,JMM會把該線程對應的本地內存中的共享變量值刷新到主內存(注意不僅僅是一個volatile變量,是所有共享變量)

可見性

可見性的意思是當一個線程修改一個共享變量時,另外一個線程能讀到這個修改的值。

如果一個欄位被聲明為volatile,Java線程內存模型確保所有線程看到這個變量的值是一致的。

如何保證可見性#

我們使用X86處理器下通過工具獲取JIT編譯器生成的彙編指令來查看對volatile進行寫操作會發生什麼

Copyinstance = new Singleton();轉成彙編代碼如下

Copy0x01a3de1d: movb $0×0,0×1104800(%esi);0x01a3de24: lock addl $0×0,(%esp);我們發現了lock前綴的指令,Lock前綴的指令在多核處理器下會發生兩件事情:

JVM中將當前線程工作內存中的數據寫回主內存中(底層中是將當前處理器緩存行的數據寫回到系統內存)使得其他線程工作內存中的該地址的數據無效(底層中是這個寫回內存的操作會使在其他CPU裡緩存了該內存地址的數據無效)為了提高處理速度,處理器不直接和內存進行通信,而是先將系統內存的數據讀到內部緩存(L1,L2或其他)後再進行操作,但操作完不知道何時會寫到內存。如果對聲明了volatile的變量進行寫操作,JVM就會向處理器發送一條Lock前綴的指令,將這個變量所在緩存行的數據寫回到系統內存。但是,就算寫回到內存,如果其他處理器緩存的值還是舊的,再執行計算操作就會有問題。所以,在多處理器下,為了保證各個處理器的緩存是一致的,就會實現緩存一致性協議,每個處理器通過嗅探在總線上傳播的數據來檢查自己緩存的值是不是過期了,當處理器發現自己緩存行對應的內存地址被修改,就會將當前處理器的緩存行設置成無效狀態,當處理器對這個數據進行修改操作的時候,會重新從系統內存中把數據讀到處理器緩存裡。

重排序

重排序是指編譯器和處理器為了優化程序性能在不影響程序執行結果的前提下而對指令序列進行重新排序的一種手段。

數據依賴性

如果兩個操作訪問同一個變量,且至少一個操作是寫操作,此時這兩個操作之間就存在數據依賴性。

上面三種情況,只要重排序兩個操作的執行順序,程序的執行結果就會被改變。

前面提到過,編譯器和處理器可能會對操作做重排序。編譯器和處理器再重排序時,會遵守數據依賴性,編譯器和處理器不會改變存在數據依賴關係的兩個操作的順序。

這裡說的數據依賴性僅針對單個處理器中執行的指令序列和單個線程中執行的操作,不同處理器之間和不同線程之間的數據依賴性不會被考慮,也就是說多線程下重排序可能會影響執行的結果

重排序對多線程的影響#

現在有AB兩個線程,A線程調用writer()方法,B線程調用reader()方法。因為A線程中的操作1與操作2不具備數據依賴性,所以會發生指令重排,同樣操作3和操作4也不具備數據依賴性。當線程B在執行操作4時,能否看到線程A在操作1對共享變量a的寫入?不一定!

Copyclass ReorderExample { int a = 0; boolean flag = false; public void writer() { a = 1; // 1 flag = true; // 2 } public void reader() { if (flag) { // 3 int i = a * a; // 4 } } } 所以說多線程下重排序可能會影響執行的結果

內存屏障

內存屏障是一個 CPU 指令,不同的硬體實現內存屏障的手段不一樣,java通過屏蔽這些差異,統一由jvm來生成內存屏障的指令。

java中內存屏障的兩個作用#

阻止屏障兩側的代碼指令重排序強制將工作內存中的數據寫回主內存,讓工作內存中的數據失效。java中的內存屏障分類

LoadLoad屏障:LoadLoad屏障前面的讀操作不能與後面的所有讀操作重排序StoreStore屏障:StoreStore屏障前面的寫操作不能與後面的所有寫操作重排序LoadStore屏障:LoadStore屏障前面的讀操作不能與後面的所有寫操作重排序StoreLoad屏障:StoreLoad屏障前面的寫操作不能與後面的所有讀操作重排序java中內存屏障插入策略

在每個volatile寫操作的前面插入一個StoreStore屏障在每個volatile寫操作的後面插入一個StoreLoad屏障在每個volatile讀操作的前面插入一個LoadLoad屏障在每個volatile讀操作的後面插入一個LoadStore屏障Copyclass VolatileBarrierExample { int a; volatile int v1 = 1; volatile int v2 = 2; void readAndWrite() { int i = v1;  // 第一個volatile讀 int j = v2;  // 第二個volatile讀 a = i + j; // 普通寫 v1 = i + 1;  // 第一個volatile寫 v2 = j * 2;  // 第二個 volatile寫 } …      // 其他方法}可以看出策略及其保守,但是在連續的加屏障操作中,jvm會自行進行優化,除去不必要的屏障。這就叫:首先保證正確性,然後再去追尋執行效率。

所有寫操作的後面插入StoreLoad屏障還有一個重要作用,因為寫操作之後方法可能會return,此時編譯器無法確定後面時候會有bolatile讀或寫,所以為了安全起見,編譯器通常會在這裡插入一個StoreLoad屏障。

作者: 海向出處:https://www.cnblogs.com/haixiang/p/12577103.html本站使用「CC BY 4.0」創作共享協議,轉載請在文章明顯位置註明作者及出處。

相關焦點

  • Java中volatile關鍵字概覽
    一、第一章 volatile關鍵字概覽多線程下變量的不可見性概述在多線程並發執行下,多個線程修改共享的成員變量,會出現一個線程修改了共享變量的值後,另一個線程不能直接 看到該線程修改後的變量的最新值。JMM(Java Memory Model):Java內存模型,是java虛擬機規範中所定義的一種內存模型,Java內存模型是標準化的,屏蔽掉了底層不同計算機的區別。Java內存模型(Java Memory Model)描述了Java程序中各種變量(線程共享變量)的訪問規則,以及在JVM中將變量存儲到內存和從內存中讀取變量這樣的底層細節。
  • 程式設計師:java單例模式,為什麼要加雙重鎖?為什麼要加volatile?
    3.變量為什麼要加volatile關鍵字在上面例子中volatile保證代碼指令不會被重排序,首先我們得先了解什麼是volatile關鍵字與及它的特性。volatile關鍵字的特性volatile具有可見性、有序性,不具備原子性。
  • Java中如何顯示不同時區的時間(原理詳解)
    Java中如何顯示不同時區的時間(原理詳解) 電子發燒友網 發表於 2019-01-01 14:58:00 在Java中,如何獲取不同時區的當前時間?
  • java修飾符
    protected 是最難理解的一種 Java 類成員訪問權限修飾詞,更多詳細內容請查看 Java protected 關鍵字詳解。訪問控制和繼承請注意以下方法繼承的規則:父類中聲明為 public 的方法在子類中也必須為 public。
  • Java類隔離加載實現原理是什麼?
    Java類隔離加載實現原理是什麼? JVM 提供一個全局類加載器的設置接口,直接替換全局類加載器,但無法解決多個自定義類加載器同時存在的問題。然而JVM會選擇當前類的類加載器來加載所有該類的引用的類。類隔離技術是什麼?
  • 深入分析volatile是如何實現可見性和有序性的
    volatile是不能保證原子性,但是在特定場景就是在32位處理器裡,對double和long型的變量的讀寫操作加了volatile修飾可以保證原子性。因為double和long都是64位,當JVM在32位處理器上運行時,可能會把64位的double/long型變量拆分為兩個32位的寫操作來執行,其他處理器可能讀到一個「寫了一半」的無效值,加了volatile就可以保證原子性。
  • Java高級程式設計師須知的並發編程知識
    Java語言提供的內置鎖技術:sychronizedJava語言提供了關鍵字synchronized,就是一種鎖的實現。準確的來說,這種實現是JVM幫我們實現的。synchronized關鍵字可以用來修飾方法,也可以用來修飾代碼塊。
  • JAVA專業術語面試100問
    4、java有哪些基本數據類型?5、數組有沒有length()方法?String有沒有length()方法?數組沒有length()方法,它有length屬性。String有length()方法。21、java中垃圾收集的方法有哪些?22、如何判斷一個對象是否存活?(或者GC對象的判定方法)?23、Java GC是在什麼時候,對什麼東西,做了什麼事情?24、什麼是類加載器雙親委派模型機制?
  • Java反射機制深入詳解
    ()字節碼已經加載到java虛擬機中,去得到字節碼;java虛擬機中還沒有生成字節碼 用類加載器進行加載,加載的字節碼緩衝到虛擬機中。= 0; i < m.length; i++) System.out.println(m[i].toString()); } catch (Throwable e){ System.err.println(e); } } }1 public synchronized java.lang.Object java.util.Stack.pop() 2 public java.lang.Object
  • C語言之const和volatile"究極"學習
    /a.outTXP二、volatile的用法老實說,這個關鍵字在面試題目裡面經常會出現,但是平時學習的時候,如果你沒有真正理解這其中的含義,在筆試的時候,腦袋裡面可能依稀是記得有那麼幾個結論,但是有時候吧,一緊張就把結論給忘了,也不是不可能,所以說,咋們還是老實一點,得真正把它原理搞明白才行,這樣上來戰場就不怕了,以後寫代碼也就少一點
  • 詳解Java表達式與運算符
    在java語言中,定義常量的語法如下:final 數據類型 常量名稱 = 值;其中,final是Java關鍵字,數據類型可以是Java語言支持的任何數據類型。在java語言中,運算符可分為5種類型:算術運算符、賦值運算符、關係運算符、邏輯運算符、位運算符。根據操作數的不同,運算符又分為單目運算符、雙目運算符和三目運算符。單目運算符只有一個操作數,雙目運算符有兩個操作數,三目運算符則有三個操作數。
  • C語言 volatile 關鍵字在編譯優化過程中有何作用
    volatile的作用是作為指令關鍵字,確保本條指令不會因編譯器的優化而省略,且要求每次直接讀值。 volatile變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。
  • AQS設計思想與重要欄位詳解
    注意:如果head存在,它的waitStatus保證不會被取消 */ private transient volatile Node head; /** * 等待隊列的尾部,懶初始化,之後只在enq方法加入新節點時修改 */ private transient volatile Node tail; /*- 同步狀態相關 */ /**
  • java軟體工程師的這些要求你有嗎?
    如今java是IT界數一數二的語言,許多程式設計師都想要成為java軟體工程師,那大家知道企業對於java軟體工程師的技術要求有哪些嗎?java軟體工程師的技術要求一.java技術要求:1.具有良好的Java語言基礎,面向對象編程基礎2.熟悉Struts、Hibernate、Spring等主流技術框架3.熟悉XML解析、Excel導出、文件上傳、發送E-mail等常見業務的實現二.資料庫技術要求1.掌握、運用SQLServer
  • Java經典面試題Spring是什麼 Spring框架入門詳解
    這是初級開發人員必然被問道的問題,如果你不懂Spring你就無法從事這一行業,此處僅限技術人員,公司的繼承人等其他個例不受此限制。那麼Spring是什麼呢,Spring遵循分層的結構思想什麼什麼實現了高內聚低耦合巴拉巴拉一大堆,咬文嚼字不是我的強項,直接開幹,讓你們看看Spring到底是什麼東西。
  • 學java可以做什麼?大數據前景和就業方向又是什麼樣的呢?
    學java可以做什麼?(1) Java可以用來做網站:很多大型網站都是用JSP寫的,JSP全名java server pages,這是一種動態網頁技術,比如我們熟悉的B站,很多政府網站都是用這個寫的所以想學習java的同學還可以負責網站方面的製作,這方面的崗位也比較多。
  • Java8 lambda表達式語法
    但是有一點這裡強調一下(Windows系統):目前我們工作的版本一般是java 6或者java 7,所以很多人安裝java8基本都是學習為主。這樣就在自己的機器上會存在多版本的JDK。而且大家一般是希望在命令行中執行java命令是基於老版本的jdk。但是在安裝完jdk8並且沒有設置path的情況下,你如果在命令行中輸入:java -version,屏幕上會顯示是jdk 8。
  • 電感減震器工作原理,電感減震器工作原理詳解
    導讀:電感減震器工作原理,電感減震器工作原理詳解如果汽車失去了減震器是什麼滋味?那我們完全可以聯想到古代出行工具「馬車」帶來的別樣震感。汽車減震器是為了改善汽車行駛的平順性和舒適性,對於需要經常跑崎嶇不平的山路的司機朋友來說,減震器就是非常重要的存在了。
  • Java中加載資料庫驅動的方式有幾種?背後的原理是什麼?
    Driver接口先來了解下java.sql.Driver接口,java.sql.Driver是所有JDBC驅動程序需要實現的接口。這個接口是提供給資料庫廠商使用的,不同廠商實現該接口的類名是不同的,例如MySQL 8.x的JDBC驅動的類名是:com.mysql.cj.jdbc.Driver。
  • 日產借鑑「蜜蜂」複眼原理 研發防撞技術
    近日網通社了解到日產正在借鑑「蜜蜂」等昆蟲的複眼原理,研發防撞技術,同時其將成為未來日產研發自動駕駛系統的重要組成之一。豐田最新研發的防撞技術,正是運用了這一原理,通過複眼多視角的變化,利用雷射檢測物體及障礙物位置。目前該項技術已在EPORO機器人上率先使用這一技術並進行測試,從而為未來搭載到自動駕駛車輛上提供經驗。