“嗨,阿米戈!”
“嗨,艾莉!”
“我想告訴你 volatile 修飾符。你知道那是什麼嗎?”
“跟線有關,記不太清了。”
“那就听好了。這裡有一些技術細節供您參考:”
“計算機有兩種內存:全局(普通)內存和處理器內置內存。處理器內置內存分為寄存器、一級緩存(L1)、二級緩存(L2)和第三級(L3)。”
“這些類型的內存具有不同的速度。最快和最小的內存是寄存器,然後是處理器緩存(L1、L2、L3),最後是全局內存(最慢)。”
“全局內存和處理器緩存以截然不同的速度運行,因此 Java 機器允許每個線程將最常用的變量存儲在本地線程內存(在處理器緩存中)中。”
“這個過程能以某種方式被控制嗎?”
“不完全是。所有工作都由 Java 機器完成。在優化性能方面非常智能。”
“但這就是為什麼我要告訴你這個。有一個小問題。當兩個線程使用同一個變量時,每個線程都可以在自己的本地緩存中存儲一個副本。然後一個線程可能會更改變量,但第二個可能看不到變化,因為它仍在使用自己的變量副本。”
“嗯,那有什麼辦法呢?”
“Java的創造者為這種情況提供了一個特殊的關鍵字:volatile。如果一個變量被不同的線程訪問,你需要用volatile修飾符來標記它,這樣Java機器就不會把它放入緩存中。通常情況下是這樣的看起來:“
public volatile int count = 0;
“哦,我想起來了,你已經說過了,我已經知道了。”
“你當然記得。但只有當我告訴你時你才記得。”
“呃,嗯,有點忘記了。”
“重複是學習之母!”
“這裡有幾個關於volatile修飾符的新事實。volatile修飾符只保證變量被安全地讀寫,不保證它被安全地改變。”
“有什麼不同?”
“看看變量是如何改變的:”
代碼 | 真正發生了什麼: | 描述 |
---|---|---|
|
|
步驟 1. 變量 count 的值從全局內存複製到處理器寄存器。 步驟 2. 第 3 步。 |
“哇!這麼說,所有變量都只在處理器中改變了?”
“是的。”
“這些值被來回複製:從內存到處理器再复制回來?”
“是的。”
“volatile 修飾符保證當變量 count 被訪問時,它將從內存中讀取(第 1 步)。如果一個線程想要分配一個新值,它肯定會在全局內存中(第 3 步)。”
“但是 Java 機器並不能保證在步驟 1 和步驟 3 之間不會有任何線程切換。”
“那麼,變量加1實際上是三個操作?”
“是的。”
“如果兩個線程同時要執行count++,那麼它們會互相干擾嗎?”
“是的,檢查一下:”
線程 1 | 線程 2 | 結果 |
---|---|---|
|
|
|
“所以,你可以訪問變量,但改變它仍然有風險?”
“嗯,可以改的,小心點就好☺”
“如何?”
“同步 是我們最好的朋友。”
“我懂了。”
GO TO FULL VERSION