互聯網面試題之請談談你對volatile的理解

JUC(java.util.concurrent)

併發和並行

volatile 是Java虛擬機提供的輕量級的同步機制

volatile三大特性

  • 保證可見性
  • 不保證原子性
  • 禁止指令重排序

JMM你談談

JMM(Java Memory Model,Java內存模型,簡稱JMM),本身是一種抽象的概念並不真實存在。它描述的是一組規則或規範,通過這組規範定義了程序中各個變量(包括實例字段,靜態字段和構成數組對象的元素)的訪問方式。

JMM關於同步的規定

  • 線程解鎖前,必須把共享變量的值刷新回主內存
  • 線程加鎖前,必須讀取主內存的最新值到自己的工作內存
  • 加鎖解鎖是同一把鎖

由於JVM運行程序的實體是線程,而每個線程創建時JVM都會爲其創建一個工作內存(有些地方稱爲棧空間),工作內存是每個線程的私有區域,而Java內存模型中規定所有變量都存儲在主內存,主內存是共享內存區域,所有線程都可以訪問,但線程對變量對操作(讀取賦值等)必須在工作內存中進行,首先要將變量從主內存拷貝到自己工作內存空間,然後對變量進行操作,操作完成後再將變量寫回主內存,不能直接操作主內存中的變量,各個線程中的工作內存中存儲着主內存的變量副本拷貝,因此不同的線程間無法訪問對方的工作內存,線程間的通信(傳值)必須通過主內存來完成,其簡要訪問過程如下圖:

JMM 規範三大特性

  • 可見性
  • 原子性
  • 有序性

volatile遵循了JMM規範的可見性和有序性但不保證原子性。

硬盤 < 內存 < CPU
內存和CPU之間存在緩存cache

CPU-Z 工具

volatile 禁止指令重排序

JMM規範中的有序性,計算機在執行程序時,爲了提高性能,編譯器和處理器常常會對指令做重排,一般分爲以下3種:
源代碼 - 編譯器優化的重排 - 指令並行的重排 - 內存系統的重排 - 最終執行的指令

  • 單線程環境裏面確保程序最終執行結果和代碼順序執行的結果一致(單線程環境下,指令重排不影響程序最終結果)
  • 處理器在進行重排序時必須要考慮指令之間的數據依賴性
  • 多線程環境中線程交替執行,由於編譯器優化重排的存在,兩個線程中使用的變量能否保證一致性是無法確定的,結果無法預測。

volatile 實現禁止指令重排序,從而避免多線程環境下程序出現亂序執行的現象。

內存屏障(Memory Barrier) 又稱內存柵欄,是一個CPU指令,它的作用有兩個:
一是保證特定操作的執行順序
二是保證某些變量的內存可見性(利用該特性實現volatile的內存可見性)。

由於編譯器和處理器都能執行指令重排序,如果在指令間插入一條Memory Barrier 則會告訴編譯器和CPU,不管什麼指令都不能和這條Memory Barrier 指令重排序,也就是說 通過插入內存屏障禁止在內存屏障前後的指令執行重排序優化。內存屏障的另外一個作用是強制刷出各種CPU的緩存數據,因此任何CPU上的線程都能讀取到這些數據的最新版本。

線程安全性獲得保證

工作內存與主內存同步延遲現象導致的可見性問題,可以使用synchronized 和 volatile 關鍵字解決,它們都可以使一個線程修改後的變量立即對其他線程可見。

對於指令重排導致的可見性問題和有序性問題,可以利用volatile關鍵字解決,因爲volatile的另外一個作用就是禁止重排序優化。

可見性代碼驗證

你在哪些地方用過volatile?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章