一篇文章徹底搞懂Synchronized 和 Volatile,看完漲薪50%!
故事背景
小米是一家互聯(lián)網(wǎng)大廠的Java開發(fā)工程師,最近在準備面試題。他的朋友小明在另一家公司面試Java后端工程師,面試回來一臉生無可戀:“面試官上來就問 synchronized 和 volatile 的區(qū)別,我結(jié)巴了半天,最后被懟得體無完膚!” 小米哈哈大笑:“這個問題很好回答啊,我來給你講講?!?于是,一場關(guān)于 Java 并發(fā)的討論就此展開……
圖片
synchronized 和 volatile 的基本概念
小米清了清嗓子,開始講解:
“我們寫 Java 代碼時,多線程編程是繞不開的一個話題,而 synchronized 和 volatile 這兩個關(guān)鍵字,都是 Java 提供的線程同步手段?!?/p>
1、synchronized:獨占鎖,保證原子性和可見性
- synchronized 是 Java 提供的關(guān)鍵字,它表示只有一個線程可以獲取作用對象的鎖,進入同步代碼塊,其他線程必須等待。
- 這樣可以防止多個線程并發(fā)修改共享資源,保證變量的可見性和原子性,防止線程安全問題。
2、volatile:內(nèi)存可見性,防止指令重排序
volatile 關(guān)鍵字用于修飾變量,它告訴 CPU 和編譯器:
- 這個變量不能被緩存,必須從主存(內(nèi)存)中讀取。
- 禁止指令重排序,防止CPU對代碼執(zhí)行順序進行優(yōu)化,導(dǎo)致多線程環(huán)境下的程序異常。
小明點點頭:“聽起來好像都能用來解決并發(fā)問題???它們的區(qū)別到底在哪?”
synchronized 和 volatile 的核心區(qū)別
小米笑著說:“這正是面試官的坑點!雖然它們都跟線程安全有關(guān),但作用完全不同?!?/p>
1、修飾對象的范圍不同
小米舉例:“比如你有一個 count 變量,你可以用 volatile 讓它的修改對所有線程可見。但如果你要保證 count++ 這個操作的原子性,那 volatile 就不行了,必須用 synchronized。”
2、是否保證原子性
小明皺眉:“等等,那如果 volatile 不能保證原子性,它的用武之地在哪?”
小米解釋:“volatile 適用于單一變量的狀態(tài)標記,比如雙重檢查鎖(DCL)模式下的 instance 變量,或者是 boolean flag 這樣的簡單開關(guān)變量。而 synchronized 適用于復(fù)雜邏輯操作,比如 count++ 這種需要原子性保護的操作。”
synchronized 和 volatile 的其他區(qū)別
小米繼續(xù)深入講解:
1、是否會造成線程阻塞
圖片
2、是否會被編譯器優(yōu)化
圖片
小明驚訝:“原來 Java 還這么智能,synchronized 還能被優(yōu)化?”
小米點頭:“對的!Java 1.6 以后,synchronized 進行了很多優(yōu)化,比如:
- 偏向鎖:如果一個線程一直在使用同一個鎖,JVM 就不會頻繁地加鎖和釋放鎖。
- 輕量級鎖:多個線程嘗試競爭鎖時,不會立即進入阻塞狀態(tài),而是使用 CAS 方式嘗試加鎖,提高性能。
- 鎖消除、鎖膨脹:JVM 會根據(jù)實際情況優(yōu)化鎖的使用?!?/li>
面試中如何回答 synchronized 和 volatile 的區(qū)別
小米總結(jié)了一套“黃金答題模板”:
“如果面試官問 synchronized 和 volatile 的區(qū)別,你可以這么答:
- volatile只能修飾變量,synchronized 既能修飾變量,也能修飾方法和代碼塊。
- volatile保證變量的可見性,但不保證原子性;synchronized同時保證可見性和原子性。
- volatile不會造成線程阻塞,而 synchronized 可能會導(dǎo)致線程阻塞。
- volatile不能被編譯器優(yōu)化,而 synchronized 通過 JVM 的優(yōu)化(如偏向鎖、輕量級鎖)能提高性能。
- volatile 適用于狀態(tài)標記等簡單場景,而 synchronized 適用于臨界區(qū)保護、多個操作組合的場景?!?/li>
小明眼睛一亮:“聽你這么一說,我感覺能答出個八九不離十了!”
真實場景下的選擇
小米最后補充道:“不過,面試官可能還會問你——在真實項目中該怎么選? 你可以告訴他:”
- 如果只是想讓變量的修改對所有線程可見,且不涉及復(fù)合操作(如count++),可以用 volatile。
- 如果需要保證線程安全,操作需要原子性,那就用 synchronized(或者 Lock 機制)。
- 如果涉及高并發(fā),還可以考慮 ReentrantLock,它比 synchronized 更靈活。
面試官的 “坑” 及應(yīng)對
小米最后提醒:“面試官喜歡挖坑,比如問你:”
1、volatile 適用于哪些場景?
- 適用于狀態(tài)標記(比如 boolean flag),單例模式的雙重檢查鎖(DCL)。
2、為什么 volatile 不能保證原子性?
- 因為 volatile 只是保證了線程可見性,但 count++ 這樣的操作是 讀取-計算-寫入,中間有多個步驟,volatile 無法保證其不被其他線程干擾。
3、為什么 synchronized 能保證原子性?
- 因為 synchronized 會讓線程獨占鎖,保證操作的完整性,其他線程必須等待鎖釋放。