深度剖析Java的volatile實現原理,再也不怕面試官問了

上篇文章我們講了synchronized的用法和實現原理,我們總愛說synchronized是重量級鎖,volatile是輕量級鎖 。為什么volatile是輕量級鎖,體現在哪些方面?以及volatile的作用和實現原理是怎樣的?本篇帶你一塊學習一下 。
1. volatile是什么?volatile是Java提供的一種輕量級的同步機制 。與synchronized修飾方法、代碼塊不同,volatile只用來修飾變量 。并且與synchronized、ReentrantLock等重量級鎖不同的是,volatile更輕量級,因為它不會引起線程上下文的切換和調度 。
2. volatile的作用說volatile作用之前 , 先說一下并發編程的三大特性:原子性、可見性和有序性 。

  • 原子性
    即一個或者多個操作作為一個整體,要么全部執行,要么都不執行,并且操作在執行過程中不會被線程調度機制打斷;而且這種操作一旦開始,就一直運行到結束 , 中間不會有任何上下文切換 。
  • 可見性
    可見性是指當多個線程訪問同一個變量時 , 一個線程修改了這個變量的值 , 其他線程能夠立即看得到修改的值 。
  • 有序性
    為了提高程序的執行效率 , 編譯器會對編譯后的指令進行重排序 , 即代碼的編寫順序不一定就是代碼的執行順序 。
并發編程中只有同時滿足這三大特性,才能保證程序正確的執行 。而volatile的只保證了可見性和有序性,不保證原子性 。
volatile的作用只有兩個:
  1. 保證內存的可見性
  2. 禁止JVM內存重排序(保證有序性)
在并發多線程情況下 , 為什么會有可見性問題?如果不做控制,為什么一個線程修改了共享變量的值,其他線程不能立即看到?這就需要聊到JMM(Java內存模型,Java Memory Model) 。
3. JMM是什么JMM(Java內存模型,Java Memory Model)定義程序訪問變量的規范,為了屏蔽不同操作系統之間的差異 。
由于Java共享變量是存儲在主內存中,而Java線程無法直接訪問主內存中數據,只能把主內存中的數據讀到本地內存(相當于拷貝一份副本) , 修改完本地內存的數據,再寫回主內存 。而此時另一個線程也把主內存的數據拷貝到自己私有的本地內存中,雖然線程1已經修改了主內存從數據,線程2卻無法感知到 , 所以就出現了內存可見性問題 。
深度剖析Java的volatile實現原理,再也不怕面試官問了

文章插圖
4. 可見性問題JMM定義的這套模型 , 會有可見性問題 。當線程1修改了本地內存的數據 , 并刷會主內存中,其他線程中本地內存的數據并沒有變化 。也就是一個線程修改了共享變量的值,其他線程無法立即感知到 。
深度剖析Java的volatile實現原理,再也不怕面試官問了

文章插圖
【深度剖析Java的volatile實現原理,再也不怕面試官問了】像上圖的流程,兩個線程都把count=0的變量拷貝到自己私有的本地內存中,線程1把count的值修改為1,并寫回主內存,而線程2本地內存的count值還是0 。
那么volatile是怎么解決可見性問題呢?
volatile主要通過匯編lock前綴指令,它會鎖定當前內存區域的緩存(緩存行),并且立即將當前緩存行數據寫入主內存(耗時非常短) , 回寫主內存的時候會通過MESI協議使其他線程緩存了該變量的地址失效,從而導致其他線程需要重新去主內存中重新讀取數據到其工作線程中 。
什么是MESI協議?
MESI協議(Modified Exclusive Shared Or Invalid)是各處理器訪問緩存時都遵循一致性協議 。核心思想是:
當CPU寫數據時,如果發現操作的變量是共享變量,即在其他CPU中也存在該變量的副本,會發出信號通知其他CPU將該變量的緩存行置為無效狀態 , 因此當其他CPU需要讀取這個變量時 , 發現自己緩存中緩存該變量的緩存行是無效的,那么它就會從內存重新讀取 。
MESI分別代表緩存行數據所處的四種狀態,通過對這四種狀態的切換,來達到對緩存數據進行管理的目的 。
狀態描述監聽任務M 修改(Modify)該緩存行有效,數據被修改了 , 和內存中的數據不一致,數據只存在于本緩存行中緩存行必須時刻監聽所有試圖讀該緩存行相對應的內存的操作,其他緩存須在本緩存行寫回內存并將狀態置為E之后才能操作該緩存行對應的內存數據E 獨享、互斥(Exclusive)該緩存行有效,數據和內存中的數據一致,數據只存在于本緩存行中緩存行必須監聽其他緩存讀主內存中該緩存行相對應的內存的操作,一旦有這種操作,該緩存行需要變成S狀態S 共享(Shared)該緩存行有效 , 數據和內存中的數據一致,數據同時存在于其他緩存中緩存行必須監聽其他緩存是該緩存行無效或者獨享該緩存行的請求,并將該緩存行置為I狀態I 無效(Invalid)該緩存行數據無效無而MESI協議是通過總線嗅探技術實現的:

推薦閱讀