核心作用:解決可見性和有序性問題
volatile
?的主要作用可以歸結為兩點:
? ? ? ? ? ? ? ? 1.保證變量的可見性?和?禁止指令重排序。
? ? ? ? ? ? ? ? 2.它提供了一種輕量級的同步機制,
? ? ? ? ? ? ? ? 3.但需要注意的是,它不能保證原子性。
保證可見性:
什么是可見性問題?
? ? ? ? 1.在 Java 內存模型中,每個線程都有自己的工作內存。
? ? ? ? 2.線程操作變量時,通常會先從主內存拷貝一份到自己的工作內存,
? ? ? ? ? ?操作完成后再刷回主內存。
問題在于:一個線程修改了自己工作內存中的變量副本,在沒有同步機制的情況下,其他線程可能無法立即看到這個修改,讀到的仍然是舊值。
volatile
?如何解決?
????????????????寫操作:當寫一個?volatile
?變量時,
????????????????????????? ? ??JVM 會立即將該線程工作內存中的新值強制刷新到主內存中。
????????????????讀操作:當讀一個?volatile
?變量時,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? JVM 會使該線程工作內存中的緩存失效,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 從而強制它從主內存中重新讀取最新的值。
這樣就保證了,一旦某個線程修改了?volatile
?變量,這個新值對其他所有線程來說都是立即可見的。它解決了線程間的可見性問題。
禁止指令重排序?
什么是指令重排序問題?
? ? ? ? ? ? ? ? 1.為了優化性能,編譯器和處理器常常會對指令的執行順序進行重排序(在不改變單線
? ? ? ? ? ? ? ? ? ?程程序語義的前提下)。
????????????????2.在單線程下沒問題,但在多線程環境下,這種亂序執行可能導致意想不到的錯誤。
volatile
?如何解決?
????????????????過添加內存屏障(Memory Barrier)來實現。? ?
? ??????????????在寫?volatile
?變量時,會在寫操作后插入一個“寫屏障”,
? ? ? ? ? ? ? ? 確保該變量之前的所有操作都已經完成,并且結果對其他線程可見。
????????????????這些屏障阻止了編譯器和水線將?volatile
?變量的操作與其他內存操作進行重排序,
????????????????從而保證了有序性。
?不能保證原子性?
這是一個非常關鍵的限制,也是?volatile
?最常見的誤用點
什么是原子性?
????????原子性意味著一個操作是不可中斷的,
????????要么完全執行成功,要么完全不執行。
????????例如?i++
?這個操作,它實際上包含三個步驟:
????????????????讀取?i
?的值
????????????????將?i
?的值加 1
????????????????將新值寫回?i
這是一個復合操作,而不是原子操作。
為什么?
volatile
?不行?
假設兩個線程同時執行?volatileInt++
(volatileInt
?是?volatile
?變量):
線程 A 讀取?
volatileInt
?的值為 10。線程 B?也讀取?
volatileInt
?的值為 10(因為線程 A 還沒寫回,可見性保證了讀到的都是最新值,但讀取后它們就各自操作了)。線程 A 對 10 加 1 得到 11,并立即寫回主內存(由于是?
volatile
,寫成功)。線程 B 也對 10 加 1 得到 11,并寫回主內存(覆蓋了線程 A 的結果)。
最終結果不是預期的 12,而是 11。
可見性保證了它們讀到的都是最新的,但無法阻止它們在“讀取-修改-寫回”這個過程中被中斷。要解決這個問題,必須使用鎖(synchronized
)或原子類(如?AtomicInteger
)。