死磕Volatile底層源碼,這篇就足矣

2020-12-17 願編程是詩

前言

本文章,讓我們一起來探討volatile關鍵字的作用。不知大家有沒有發現沒有,平時在練習或者學習Java編程時,volatile關鍵字幾乎沒有用到過,而平時在看開源項目時,經常會看某些類變量定義為volatile,今天我們來解釋volatile的重要性。

閱讀導航

一、volatile關鍵字的作用

二、volatile如何保證可見性?

三、volatile源碼剖析

四、原子性

五、CAS

volatile關鍵字,是用來在多處理器開發中保證共享變量的「可見性」。也就是說當一個線程修改一個共享變量值時,其他所有線程就能立刻獲得修改以後的值。

二、是如何保證可見性?

volatile進行寫操作時,CPU會做什麼事情呢?又如何保證可見性?

當volatile變量修飾的共享變量進行寫操作的時候,JVM就會向處理器發送一條Lock前綴的指令,然後將這個變量所在緩存行的數據寫回到系統內存,為了保證各個處理器的緩存是一致,這時候就會實現緩存一致性協議。

實現緩存協議是指每個處理器通過在總線上傳播的數據來檢查自己緩存的值是否過期,當發現自己緩存行對應的內存地址被修改,則將緩存行設置成無效狀態;當處理器對這個數據進行修改操作的時候,會重新從系統內存中把數據讀到處理器緩存裡頭。

JVM是如何在JMM層面解決原子性、有序性、可見性問題的呢

原子性:Java中提供了兩個高級指令 monitorenter和 monitorexit,也就是對應的synchronized同步鎖來保證原子性。可見性:volatile、synchronized、final都可以解決可見性問題有序性:synchronized和volatile可以保證多線程之間操作的有序性,volatile會禁止指令重排序三、源碼剖析

編寫一個演示案例來分析volatile

查看字節碼,當被volatile修飾的關鍵字,會多一個ACC_VOLATILE

四、什麼是原子性

多核CPU架構下,在同一時刻對同一共享變量執行decl指令這個指令涉及到兩次內存操作,這就是原子性問題,在32處理器,提供了兩種鎖的機制來保證原子性。

使用總線鎖保證原子性使用緩存鎖保證原子性

五、什麼是CAS

CAS是無鎖優化,或者叫自旋。它的基本操作是循環進行CAS操作來保證線程安全的。

但是CAS實現原子操作時有三大問題

ABA問題:因為CAS需要在操作值的時候,檢查值有沒有發生變化,如果沒有發生變化則更新。解決思路就是在變量前面使用版本號,每次變量更新的時候把版本號加1即可。(Java 1.5開始,JDK的Atomic包裡提供了一個類AtomicStampedReference來解決ABA問題)循環時間長開銷大:自旋CAS如果長時間不成功,會給CPU帶來非常大的執行開銷。只能保證一個共享變量的原子操作:也就是說當對多個共享變量操作時,循環CAS就無法保證操作的原子性。解決方案:可以使用鎖、可以把多個變量放在一個對象裡來進行CAS操作。

相關焦點

  • 從底層原理深度剖析volatile關鍵字,徹底徵服面試官
    本篇文章從底層原理層面深度剖析volatile關鍵字是如何實現內存可見性的,同時引入了Java內存模型、指令重排序以及內存屏障等知識點作為原理分析的知識支撐。閱讀本文之前,推薦大家先閱讀作者之前的一篇關於happens-before的文章,這樣更有助於大家對volatile關鍵字底層原理的理解。
  • 死磕 java所有集合之終結篇
    點擊下面連結可以直接到相應的章節查看:死磕 java集合之HashMap源碼分析死磕 java集合之LinkedHashMap源碼分析死磕 java集合之WeakHashMap源碼分析死磕 java集合之TreeMap源碼分析(一)死磕 java集合之TreeMap源碼分析(二)死磕 java集合之TreeMap
  • 吃透這篇ConcurrentHashMap源碼,就夠了
    前言學習ConcurrentHashMap之前,我們還是先來複習一下CAS、volatile、樂觀鎖與悲觀鎖的知識吧(本文使用JDK1.8+)。二、volatile介紹volatile變量是一個更輕量級的同步機制,因為在使用這些變量時不會發生上下文切換和線程調度等操作,但是volatile變量也存在一些局限,比如不能用於構建原子的複合操作,因此當一個變量依賴舊值時就不能使用volatile變量,volatile內部已經做了synchronized。
  • 拼多多JDK源碼大揭秘,由淺入深看源碼,探究Java並發原理
    寫在前面幾乎所有的大神都會強調看源碼,也強調源碼的重要性;但是如何看源碼,源碼看什麼?看了什麼用?看了怎麼用?困擾很多人,尤其是初學者。如何閱讀源碼,是每個程式設計師需要面臨的一項挑戰。為什麼需要閱讀源碼?
  • 詳解鎖原理,synchronized、volatile+cas底層實現
    當嘗試給資源加鎖卻被其他線程先鎖定時,不是阻塞等待而是循環再次加鎖在鎖常被短暫持有的場景下,線程阻塞掛起導致CPU上下文頻繁切換,這可用自旋鎖解決;但自旋期間它佔用CPU空轉,因此不適用長時間持有鎖的場景2 synchronized底層原理代碼使用synchronized加鎖,在編譯之後的字節碼是怎樣的呢
  • 從面試角度分析CopyOnWriteArrayList源碼
    接下來,我們先來簡單分析一下Vector的源碼。一、Vector集合源碼簡析由於本文的重點不是Vector集合,因此只是簡單的分析一下Vector的初始化方法和添加元素的方法。Vector的底層實現和ArrayList一樣,都是由數組實現的。
  • 死磕Synchronized底層實現--重量級鎖
    作者:farmerjohngit連結:https://github.com/farmerjohngit本文為死磕Synchronized底層實現第四篇文章,內容為重量級鎖實現。本系列文章將對HotSpot的synchronized鎖實現進行全面分析,內容包括偏向鎖、輕量級鎖、重量級鎖的加鎖、解鎖、鎖升級流程的原理及源碼分析,希望給在研究synchronized路上的同學一些幫助。
  • 深入synchronized和volatile底層原理
    假設一種情況CPU1和CPU2都要使用主存中的變量A (A=1),它們兩都拷貝了一份數據A到自己的緩存中;這三個步驟操作了過後與預期結果不一致,預期結果不是A=3嗎,這可咋個整?總線鎖為了解決緩存一致性問題,最開始使用Bus Locking(總線鎖)。
  • 再有人問你volatile是什麼,把這篇文章也發給他.
    在上一篇文章中,我提到過:volatile只能保證可見性和有序性,無法保證原子性。關於這部分內容,有讀者閱讀之後表示還是不是很理解,所以我再單獨寫一篇文章深入分析一下。閱讀本文之前,請先閱讀上一篇文章:再有人問你volatile是什麼,就把這篇文章發給他在上一篇文章中我們提到過:volatile一個強大的功能,那就是他可以禁止指令重排優化。
  • 從源碼解讀Mybatis的初始化過程
    引言這篇文章呢,主要是講Mybtais的兩種方式的源碼剖析:傳統方式以及Mapper代理方式,初次探索Mybatis源碼,希望大佬勿噴並且指正錯誤,謝謝!2.主要構件及其相互關係3.層次結構圖下面這張圖就是Mybatis流程的層次結構圖,建議讀源碼的時候記得打開這張圖,思路才不會亂呢
  • 深入分析volatile是如何實現可見性和有序性的
    volatile是不能保證原子性,但是在特定場景就是在32位處理器裡,對double和long型的變量的讀寫操作加了volatile修飾可以保證原子性。對有序性不太理解的同學可以先看下前這篇文章然後在該變量讀操作後面插入一個LoadStore屏障,禁止volatile讀操作與後面任意讀寫操作重排序。
  • Java程式設計師面試必備:Volatile全方位解析
    那麼,它的底層是如何保證可見性和禁止指令重排的呢?圖解volatile是如何保證可見性的?volatile是如何解決java並發中可見性的問題 volatile如何防止指令重排 volatile可以解決原子性嘛?為什麼? volatile底層的實現機制 volatile和synchronized的區別?
  • 知名公司面試題:談談你對volatile關鍵字的理解
    對於一名java開發者,不管是在求職面試還是項目實際開發中,volatile都是一個需要掌握的知識點,是需要掌握好的。我們平時在閱讀源碼的過程中,時常會遇到volatile關鍵字,譬如Atomic類,通過源碼我們會發現volatile無處不在。為什麼要用到volatile關鍵字?
  • 可惜了,面試敗在了volatile關鍵字上,直擊痛點搞懂volatile
    這不,有位同學就來找我訴苦了,前兩次面試都挺順利的,到了三面竟然栽在了volatile關鍵字上。下面我們就來好好聊聊volatilevolatilevolatile 是一個類型修飾符。volatile寫底層實現JMM對volatile的內存屏障插入策略在每個volatile寫操作的前面插入一個StoreStore屏障。在每個volatile寫操作的後面插入一個StoreLoad屏障。
  • Java8線程池ThreadPoolExecutor底層原理及其源碼解析
    , 參考林迪效應;就算沒有直接用到, 可能間接也用到了類似的思想或原理, 例如tomcat, jetty, 資料庫連接池, MQ;本文不會對線程的基礎知識進行介紹, 所以最好已"進食"關於線程的基礎知識, 再"食用"本文更佳;由於在下的工作及其它原因, 前後花費了數月的時間才完成這篇博客
  • Java中volatile關鍵字概覽
    JMM(Java Memory Model):Java內存模型,是java虛擬機規範中所定義的一種內存模型,Java內存模型是標準化的,屏蔽掉了底層不同計算機的區別。Java內存模型(Java Memory Model)描述了Java程序中各種變量(線程共享變量)的訪問規則,以及在JVM中將變量存儲到內存和從內存中讀取變量這樣的底層細節。JMM有以下規定:所有的共享變量都存儲於主內存。這裡所說的變量指的是實例變量和類變量。
  • Java中volatile特性概述
    volatile特性概述volatile總體概覽在上節中,我們已經研究完了volatile可以實現並發下共享變量的可見性,除了volatile可以保證可見性外,volatile 還具備如下一些突出的特性:volatile的原子性問題:volatile
  • Java研發技術——Volatile原理詳解
    volatilevolatile 只能保證對單次讀/寫的原子性。i++ 這種符合操作操作不能保證原子性。1104800(%esi);0x01a3de24: lock addl $0×0,(%esp);我們發現了lock前綴的指令,Lock前綴的指令在多核處理器下會發生兩件事情:JVM中將當前線程工作內存中的數據寫回主內存中(底層中是將當前處理器緩存行的數據寫回到系統內存)使得其他線程工作內存中的該地址的數據無效(
  • 你應該要理解的java並發關鍵字volatile
    於是乎,今天的主角登場了,這就是volatile關鍵字。volatile關鍵字的作用很簡單,就是一個線程在對主內存的某一份數據進行更改時,改完之後會立刻刷新到主內存。並且會強制讓緩存了該變量的線程中的數據清空,必須從主內存重新讀取最新數據。
  • volatile關鍵字詳解
    ;    }}class Mythread{    //不加volatile,主線程無法得知num的值發生了改變,從而陷入死循環    volatile int num = 0;    public void increment(){        ++num;    }}