一不小心就翻車!volatile 和 Atomic 的區別,你真的清楚嗎?
引言
小米最近在準備社招面試,畢竟在互聯網這條“卷”路上,稍微停滯不前,就可能被更年輕的程序員們彎道超車。為了在面試中脫穎而出,小米最近瘋狂刷面試題,尤其是多線程相關的知識。
這天,小米和面試官約了一場線上面試,一上來面試官就丟出了一個高頻問題:
“Java 中 volatile 變量和 Atomic 變量有什么不同?”
小米心里一緊,心想:“這不就是面試八股文里常見的問題嗎?” 于是,他開始了自信的作答……
volatile 變量:能見度好,但不保證原子性
1. volatile 是什么?
小米微微一笑,說道:
volatile 關鍵字的作用是保證變量的可見性,防止指令重排序。
通俗點說,Java 的 volatile 變量類似于“全員廣播”模式。每當某個線程修改了這個變量,其他線程都能立刻看到最新的值,不會出現“臟數據”。
2. volatile 如何保證可見性?
JMM(Java 內存模型) 規定,volatile 變量在寫入時,會多做一步操作:
- 普通變量賦值: 線程先從主內存中讀取數據,復制到自己的工作內存,修改后再寫回主內存,其他線程無法感知這個變化。
- volatile 變量賦值: 直接刷新到主內存,并讓所有線程的緩存失效,從而保證所有線程都能立即看到最新的值。
就像是小米早上給群發了一條消息:“今天團建改到 3 點!” 這樣,所有人都能立即收到,而不會有人還在按照原來的時間去行動。
3. 但 volatile 不能保證原子性
面試官點了點頭,然后笑著問:“那 volatile 變量能保證原子性嗎?”
小米立刻搖頭:“不能!”
他舉了個例子:
圖片
看上去 count++ 只是一個簡單的自增操作,但實際上它是三步操作:
- 讀取 count 的值
- 執行 +1 操作
- 將新值寫回 count
如果有兩個線程 A 和 B 同時執行 increment() 方法:
- A 讀取 count=0,執行 count+1,還沒來得及寫回
- B 也讀取 count=0,執行 count+1,然后寫回
- A 最后寫回 count=1
本來執行兩次 increment(),結果 count 還是 1,丟失了一次更新!這就是“并發問題”。
所以,volatile 僅僅保證了變量的可見性,并不能保證操作的原子性!
Atomic 變量:天生線程安全,保證原子性
小米接著說道:“如果想保證原子性,Java 提供了 java.util.concurrent.atomic 包,里面的 Atomic 變量是更好的選擇。”
1. 什么是 Atomic 變量?
Atomic 變量 依靠 CAS(Compare-And-Swap) 機制來保證原子性。
還是剛才的例子,如果我們改用 AtomicInteger:
圖片
這樣,多個線程同時調用 increment() 方法時,就不會丟失數據了。
2. Atomic 如何保證原子性?
Atomic 變量的核心是 CAS(比較并交換,Compare-And-Swap),它的原理是:
- 讀取舊值 oldValue
- 計算新值 newValue
- 使用 CPU 指令 嘗試將 oldValue 更新為 newValue
- 如果 oldValue 還是舊值,就更新成功;如果已經被其他線程修改了,就重新嘗試。
這種方式避免了傳統的 鎖(synchronized) 帶來的性能問題,CAS 是無鎖并發的基礎,比 synchronized 更高效!
不過,CAS 也不是萬能的,它可能導致“ABA 問題”(變量值在中間被修改了,但最終又變回去了),解決方案是 AtomicStampedReference 這種帶版本號的原子變量。
volatile vs Atomic:到底該選誰?
面試官看小米講得這么清楚,問道:“所以,在實際開發中,volatile 和 Atomic 該怎么選呢?”
小米總結道:
圖片
如果只是需要保證變量的可見性,而不涉及并發修改,可以使用 volatile,比如:
- 線程間的標志位
- double check 機制的 instance 變量
如果需要保證變量的原子性,應該使用 Atomic 變量,比如:
- 計數器(AtomicInteger)
- 線程安全的累加器(LongAdder)
面試官的加分題
面試官滿意地點了點頭,然后問:“那 synchronized 和 Atomic 有什么不同呢?”
小米心想:“這不就是送分題嗎?”
他笑著回答:
- synchronized 是一種阻塞式的同步機制,線程會進入阻塞狀態,性能開銷較大。
- Atomic 變量是非阻塞的,它基于 CAS 操作,不會讓線程進入阻塞狀態,因此更高效。
- synchronized 適合需要多個變量一起同步的場景,而 Atomic 適合單個變量的無鎖操作。
面試官點頭:“不錯,今天的面試到這里,回去等通知吧!”
小米掛斷電話,感覺這次面試發揮得不錯,心里暗自高興。為了幫助更多正在找工作的朋友,他在朋友圈里分享了今天的面試知識點:
- volatile 只保證可見性,不保證原子性
- Atomic 變量使用 CAS 機制,保證原子性,適用于計數、累加等操作
- synchronized 適用于多個變量的同步,Atomic 適用于單個變量的高效無鎖操作