“嗨,阿米戈!”

“嗨,艾莉!”

“我想告訴你 volatile 修飾符。你知道那是什麼嗎?”

“跟線有關,記不太清了。”

“那就听好了。這裡有一些技術細節供您參考:”

“計算機有兩種內存:全局(普通)內存和處理器內置內存。處理器內置內存分為寄存器、一級緩存(L1)、二級緩存(L2)和第三級(L3)。”

“這些類型的內存具有不同的速度。最快和最小的內存是寄存器,然後是處理器緩存(L1、L2、L3),最後是全局內存(最慢)。”

“全局內存和處理器緩存以截然不同的速度運行,因此 Java 機器允許每個線程將最常用的變量存儲在本地線程內存(在處理器緩存中)中。”

“這個過程能以某種方式被控制嗎?”

“不完全是。所有工作都由 Java 機器完成。在優化性能方面非常智能。”

“但這就是為什麼我要告訴你這個。有一個小問題。當兩個線程使用同一個變量時,每個線程都可以在自己的本地緩存中存儲一個副本。然後一個線程可能會更改變量,但第二個可能看不到變化,因為它仍在使用自己的變量副本。”

“嗯,那有什麼辦法呢?”

“Java的創造者為這種情況提供了一個特殊的關鍵字:volatile。如果一個變量被不同的線程訪問,你需要用volatile修飾符來標記它,這樣Java機器就不會把它放入緩存中。通常情況下是這樣的看起來:“

public volatile int count = 0;

“哦,我想起來了,你已經說過了,我已經知道了。”

“你當然記得。但只有當我告訴你時你才記得。”

“呃,嗯,有點忘記了。”

“重複是學習之母!”

“這裡有幾個關於volatile修飾符的新事實。volatile修飾符只保證變量被安全地讀寫,不保證它被安全地改變。”

“有什麼不同?”

“看看變量是如何改變的:”

代碼 真正發生了什麼: 描述
count++
register = count;

register = register+1;

count = register;
步驟 1.
變量 count 的值從全局內存複製到處理器寄存器。

步驟 2.
在處理器內部,寄存器變量加 1。

第 3 步。
變量的值從處理器複製到全局內存。

“哇!這麼說,所有變量都只在處理器中改變了?”

“是的。”

“這些值被來回複製:從內存到處理器再复制回來?”

“是的。”

“volatile 修飾符保證當變量 count 被訪問時,它將從內存中讀取(第 1 步)。如果一個線程想要分配一個新值,它肯定會在全局內存中(第 3 步)。”

“但是 Java 機器並不能保證在步驟 1 和步驟 3 之間不會有任何線程切換。”

“那麼,變量加1實際上是三個操作?”

“是的。”

“如果兩個線程同時要執行count++,那麼它們會互相干擾嗎?”

“是的,檢查一下:”

線程 1 線程 2 結果
register1 = count;
register1++;
count = register1;
register2 = count;
register2++;
count = register2;
register1 = count;
register2 = count;
register2++;
count = register2;
register1++;
count = register1;

“所以,你可以訪問變量,但改變它仍然有風險?”

“嗯,可以改的,小心點就好☺”

“如何?”

同步 是我們最好的朋友。”

“我懂了。”