Java內存模型與volatile關鍵字

2020-12-17 IT樂知

Java的內存模型大概樣子還是有必要了解下的,今天就學習了下,順便學習了一點volatile關鍵字!

Java內存模型

主內存中存儲一些可以共享的變量比如實例欄位、靜態欄位和構成數組對象的元素,但是不包括局部變量與方法參數,因為它們是線程私有的,不會被共享。

每個線程獲取這類變量都是先把變量從主內存加載到工作內存,然後才能進行使用,同樣如果是修改,那麼也是先修改了工作內存中的變量,然後再從工作內存同步進主內存中。

多線程不安全

這種模式就會出現多線程不安全問題,如果兩個線程同時操作數據,如下圖:

兩個線程同時對主內存中變量進行操作,他們都會把變量先加載到自己的工作內存中去,然後對他進行操作,如果並發執行,線程2就會覆蓋線程1的操作。

關鍵字volatile

一個變量如果被volatile修飾那麼他有兩個特性

1、變量對所有線程的可見性,意思是如果一條線程修改了這個變量的值,那麼其他線程就可以立刻知道的。

2、禁止指令重排序優化,JVM在編譯Java代碼、或者CPU在執行JVM字節碼時,對現有的字節碼指令順序進行重新排序。

特性1的可見性解釋如下圖:

volatile修飾的變量在被修改後會處理器直接將結果stroe和write進主內存,同時使得其他線程的工作內存緩存失效,這樣就實現了所謂的可見性!

volatile同樣不安全

但是如果這樣就能保證數據的安全了嗎?請看下面的示例:

30個線程都對共享變量shareVariable進行了10000次自增。但是最終的列印結果卻不是30*10000,這是因為volatile只保證了變量的可見性,並不保證變量的原子性

通過之前的字節碼學習,我們知道「i=i++」需要用到多行字節碼指令,並且一個字節碼指令可能包含不止一條機器碼指令,因此「i=i++」並不具有原子性。所以shareVariable變量並沒有達到我們想要的結果。

也就是說即使通過volatile把變量的修改及時同步到其他線程的工作內存,但是由於對變量的操作並不是原子行,比如最簡單的「shareVariable++;」可以看下他的字節碼如下圖:

在字節碼層面也經歷了:從常量池加載i,加載常量1,然後add指令對他們計算,再put進常量池中,一共4個步驟,而機器碼就更加多了。

volatile使用場景

那麼volatile這個欄位又有什麼用呢?

由於volatile變量只能保證可見性,並不能保證原子性,不過在以下場景還是可以使用的:

1、運算結果並不依賴變量的當前值,或者能夠確保只有單一的線程修改變量的值。

2、變量不需要與其他的狀態變量共同參與不變約束。

第一條保證了變量的準確性,第二條則是防止變量參與約束時又產生原子性問題,比如上面那個代碼「if (shareVariableBoolean)」再加一些判斷條件,新加的條件可能就會破壞原子性問題。

所以在不符合以下兩條規則的運算場景中,我們還是要通過加鎖 (使用synchronized、java.util.concurrent中的鎖或原子類)來保證原子性。

總結

今天只是簡單的了解了下Java的內存模型和volatile關鍵字,不過也知道了volatile關鍵字並不能保證線程安全,但是在面試中好像經常有這一問!

Java程式設計師日常學習筆記,如理解有誤歡迎各位交流討論!

相關焦點

  • 你應該要理解的java並發關鍵字volatile
    提高java的並發編程,就不得不提volatile關鍵字,不管是在面試還是實際開發中 volatile都是一個應該掌握的技能。他的重要性不言而喻。因此也有必要學好。一、為什麼要用到volatile關鍵字?
  • Java中volatile關鍵字概覽
    一、第一章 volatile關鍵字概覽多線程下變量的不可見性概述在多線程並發執行下,多個線程修改共享的成員變量,會出現一個線程修改了共享變量的值後,另一個線程不能直接 看到該線程修改後的變量的最新值。變量不可見性內存語義概述在介紹多線程並發修改變量不可見現象的原因之前,我們需要了解回顧一下Java內存模型(和Java並發編程有關的模 型):JMM。
  • 1分鐘讀懂java中的volatile關鍵字
    本文將以儘量簡潔的方式介紹java中的volatile關鍵字。如果覺得寫的不錯,記得,如果寫的不好歡迎批評指正,讓我們一起進步!volatile作為java中的關鍵詞之一,用以聲明變量的值可能隨時會別的線程修改,使用volatile修飾的變量會強制將修改的值立即寫入主存,主存中值的更新會使緩存中的值失效(非volatile變量不具備這樣的特性,非volatile變量的值會被緩存,線程A更新了這個值,線程B讀取這個變量的值時可能讀到的並不是是線程A更新後的值)。
  • Java並發之volatile關鍵字內存可見性問題
    Java並發之volatile關鍵字內存可見性問題線程之間數據共享案例我們先來看一個場景:Main函數啟動後,調用一個線程向list中添加數據。而主內存(也就是系統內存非程序自己需要的內存)flag變量對所有共享這個變量的線程來說,都應該是可見的才可以。那麼這個時候,在Java中怎麼實現線程之間共享數據的內存可見性呢?這裡就是我們今天需要講解的關鍵字:volatile。
  • volatile關鍵字詳解
    點擊上方藍色字體,選擇「標星公眾號」優質文章,第一時間送達  作者 |  Zzzkis來源 |  urlify.cn/AVVFNr76套java>如上述代碼,如果不加volatile,程序運行結果如下加上volatile關鍵字後,程序運行結果如下
  • Java中 volatile 關鍵字的最全總結,趕快給自己查缺補漏吧!
    在Java內存模型中,為了效率是允許編譯器和處理器對指令進行重排序,當然重排序不會影響單線程的運行結果,但是對多線程會有影響。Java提供volatile來保證一定的有序性。最著名的例子就是單例模式裡面的DCL(雙重檢查鎖)。
  • 知名公司面試題:談談你對volatile關鍵字的理解
    作為一名java程式設計師,求職面試時,關於volatile關鍵字時常會遇到。張工最近到某知名網際網路公司面試,面試官提出這樣的一個問題:談談你對volatile關鍵字的理解張工一時間沒有回答上來,面試官:你都工作三年了,怎麼對volatile關鍵字都沒掌握啊。
  • 筆記07 - Java內存模型
    Java的內存模型如果任由CPU進行優化或多線程的操作,會導致Java程序運行結果出乎意料,為了解決這種問題,JVM推出了Java內存模型來解決。在java內存模型中,我們統一使用工作內存(working memory)來當作CPU中的寄存器或高速緩存的抽象。
  • 教學筆記:這樣來學習Java volatile關鍵字
    相信大多數Java程式設計師都學習過volatile這個關鍵字的用法。百度百科上對volatile的定義:volatile是一個類型修飾符(type specifier),被設計用來修飾被不同線程訪問和修改的變量。volatile的作用是作為指令關鍵字,確保本條指令不會因編譯器的優化而省略,且要求每次直接讀值。
  • 就是要你懂 Java 中 volatile 關鍵字實現原理
    前言我們知道volatile關鍵字的作用是保證變量在多線程之間的可見性,它是java.util.concurrent包的核心,沒有volatile就沒有這麼多的並發類給我們使用本文詳細解讀一下volatile關鍵字如何保證變量在多線程之間的可見性,在此之前,有必要講解一下CPU緩存的相關知識,掌握這部分知識一定會讓我們更好地理解volatile的原理,從而更好、更正確地地使用volatile關鍵字。
  • 深度解析volatile關鍵字,就是這麼簡單
    .dylib關鍵字volatile是Java虛擬機提供的最輕量級的同步機制,當一個變量被關鍵字volatile修飾之後,它有如下兩個特性:保證了這個變量對所有線程的可見性禁止指令重排序優化關鍵字volatile可以保證變量對所有線程的可見性,也就是當一個線程修改了這個變量的值,其他線程能夠立即得到修改的值。
  • Java並發編程徹底搞懂volatile關鍵字
    本篇文章主要對volatile關鍵字進行解剖。volatile關鍵字特性內存可見性(Memory Visibility),所有線程都能看到共享內存的最新狀態;有序性;不具備原子性(最致命缺點解決辦法是使用volatile關鍵字。關鍵字volatile的作用是強制從公共堆棧中取得變量的值,而不是從線程私有數據棧中取得變量值。
  • Java程式設計師面試必備:Volatile全方位解析
    現代計算機的內存模型(計算機模型,MESI協議,嗅探技術,總線)為了更好理解volatile,先回顧一下計算機的內存模型與JMM(Java內存模型)吧~計算機模型計算機執行程序時,指令是由CPU處理器執行的,而打交道的數據是在主內存當中的。
  • java基礎之volatile關鍵字
    VolatileJava語言的關鍵字,用來聲明變量,表示這個變量是被同時運行的幾個線程修改的,需要保證變量的可見性並禁止指令重排序。可見性的含義:可見性的意思是指當一個線程修改一個共享變量時,另外一個線程能讀到這個修改後的值,volatile作用就是當一個線程修改了共享變量的值,其他線程馬上就能知道。Java內存模型規定所有的變量都是存在主存當中,每個線程都有自己的工作內存。
  • Java裡面volatile關鍵字修飾引用變量的陷阱
    Java裡面volatile關鍵字修飾引用變量的陷阱如果我現在問你volatile的關鍵字的作用,你可能會回答對於一個線程修改的變量對其他的線程立即可見。嚴謹的回答應該是volatile關鍵字對於基本類型的修改可以在隨後對多個線程的讀保持一致,但是對於引用類型如數組,實體bean,僅僅保證引用的可見性,但並不保證引用內容的可見性。
  • Java中volatile特性概述
    volatile特性概述volatile總體概覽在上節中,我們已經研究完了volatile可以實現並發下共享變量的可見性,除了volatile可以保證可見性外,volatile 還具備如下一些突出的特性:volatile的原子性問題:volatile
  • 一文讀懂Java關鍵詞之volatile作用(內存屏障)
    我們還知道Java內存模型中,各個線程還保存了一份主內存中數據的拷貝,那麼不同線程的拷貝應該如何保證數據的一致性呢,今天我們就跟大家一起看看其中的一個技術。很簡單,就是在變量前面加上volatile關鍵字,所以上面的代碼,我們修改如下:這樣當某個核心對onWork進行賦值之後,CPU會強制將這個值寫回主內存,在遇到要讀取onWork狀態的時候,必須從主內存中讀取最新的值。
  • 簡述Java內存模型
    所以我希望如果本章節出現讀者第一次接觸的名詞以及很難理解的概念時,儘量多讀幾遍,把這些概念聯想出模型來記憶,必要時反覆翻閱。2.3.1什麼是Java內存模型在介紹Java內存模型(JMM)前,我要打消讀者一個錯誤的認知,那就是JMM與JVM到底是什麼關係,現在告訴大家,Java虛擬機模型(JVM)與Java內存模型(JMM)沒有本質上的聯繫。
  • Java內存模型原理,你真的理解嗎?
    這篇文章主要介紹模型產生的問題背景,解決的問題,處理思路,相關實現規則,環環相扣,希望讀者看完這篇文章後能對 Java 內存模型體系產生一個相對清晰的理解,知其然知其所以然。在介紹 Java 內存模型之前,我們先了解一下物理計算機中的並發問題,理解這些問題可以搞清楚內存模型產生的背景。
  • 從底層原理深度剖析volatile關鍵字,徹底徵服面試官
    本篇文章從底層原理層面深度剖析volatile關鍵字是如何實現內存可見性的,同時引入了Java內存模型、指令重排序以及內存屏障等知識點作為原理分析的知識支撐。閱讀本文之前,推薦大家先閱讀作者之前的一篇關於happens-before的文章,這樣更有助於大家對volatile關鍵字底層原理的理解。