服務器網(wǎng)站部署嘉興網(wǎng)絡推廣
一、Java內(nèi)存模型(JMM)是什么?
Java內(nèi)存模型(Java Memory Model, JMM)是Java多線程編程中共享內(nèi)存的訪問規(guī)則,定義了線程如何與主內(nèi)存(Main Memory)和工作內(nèi)存(Work Memory)交互,解決多線程并發(fā)中的原子性、可見性、有序性問題。確保多線程程序在不同架構(gòu)的處理器和內(nèi)存系統(tǒng)上都能正確運行。
二、核心概念
-
主內(nèi)存與工作內(nèi)存
-
主內(nèi)存:所有線程共享的內(nèi)存區(qū)域,存儲變量(實例字段、靜態(tài)字段等),類似于計算機中的物理內(nèi)存。
-
工作內(nèi)存:每個線程私有的內(nèi)存區(qū)域,存儲主內(nèi)存變量的副本。
-
交互規(guī)則:線程只能操作工作內(nèi)存中的變量副本,修改后需同步回主內(nèi)存。
-
-
原子性、可見性、有序性
-
原子性:操作不可分割(如
AtomicInteger
),要不全部完成,要不全部不做。-
基本數(shù)據(jù)類型的賦值操作(如int a = 10)。
-
使用synchronized關鍵字或Lock接口實現(xiàn)的同步代碼塊。
public class Counter {private int count = 0; ?public synchronized void increment() {count++;} }
-
-
可見性:一個線程對變量的修改能被其他線程立刻看到(
volatile
、synchronized
)。-
volatile關鍵字:確保變量的修改立即刷新到主內(nèi)存,其他線程讀取時能獲取最新值。
-
synchronized關鍵字:線程退出同步塊時,會將工作內(nèi)存中的變量寫回主內(nèi)存。
public class VolatileExample {private volatile boolean running = true; ?public void stop() {running = false;} ?public void run() {while (running) {// do something}} }
-
-
有序性:代碼執(zhí)行順序與程序順序一致(防止指令重排序)。
-
happens-before原則:定義了操作的先后順序。
-
內(nèi)存屏障:防止指令重排序。
-
-
-
Happens-Before原則 JMM定義的保證有序性的規(guī)則,若操作A happens-before 操作B,則A的結(jié)果對B可見。 常見規(guī)則:
-
程序順序規(guī)則 :在單線程中,代碼的執(zhí)行順序符合書寫順序。
-
volatile
變量規(guī)則 -
鎖規(guī)則(
synchronized
或Lock
) -
線程啟動/終止規(guī)則
-
傳遞性規(guī)則
-
三、關鍵機制
-
volatile關鍵字
-
保證變量的可見性:直接讀寫主內(nèi)存,禁止緩存。
-
禁止指令重排序:通過插入內(nèi)存屏障(Memory Barrier)。
-
示例:雙檢鎖單例模式中的
volatile
修飾實例對象。
-
-
synchronized與鎖
-
通過監(jiān)視器鎖(Monitor)實現(xiàn)原子性和可見性。
-
線程解鎖前必須將變量同步回主內(nèi)存。
-
-
final關鍵字
-
構(gòu)造函數(shù)中對
final
域的寫入,與后續(xù)引用賦值操作不會被重排序。 -
確保其他線程看到
final
變量時,其初始化已完成。
-
-
并發(fā)工具類
如CountDownLatch、CyclicBarrier等,用于更復雜的線程同步。
四、常見問題與面試考點
-
volatile能否保證原子性?
-
不能。
volatile
僅保證讀寫操作的原子性,但復合操作(如i++
)仍需同步。
-
-
DCL(雙檢鎖)單例模式為什么要加volatile?
-
防止指令重排序?qū)е缕渌€程獲取未初始化完成的對象。
-
-
synchronized和ReentrantLock的區(qū)別?
-
synchronized
是JVM內(nèi)置鎖,自動釋放;ReentrantLock
需手動加鎖/解鎖,支持公平鎖、條件變量。
-
-
什么是內(nèi)存可見性問題?如何解決?
-
線程A修改變量后未及時同步到主內(nèi)存,線程B讀取舊值。
-
解決:
volatile
、synchronized
、Lock
。
-
-
指令重排序的典型場景?
-
單例模式初始化:
new Object()
可能被拆分為分配內(nèi)存、初始化對象、賦值引用三步,重排序后導致空指針。
-
-
什么是“線程安全的發(fā)布”?
-
對象在構(gòu)造完成后才能被其他線程訪問。
-
方式:
volatile
、靜態(tài)初始化塊、final
域、線程安全容器(如ConcurrentHashMap
)。
-
jJ五、代碼示例:內(nèi)存可見性問題
public class VisibilityDemo {// 不加volatile可能導致死循環(huán)private static volatile boolean flag = true;
?public static void main(String[] args) throws InterruptedException {new Thread(() -> {while (flag) {} // 線程可能讀取到舊的flag值System.out.println("Thread stopped.");}).start();
?Thread.sleep(1000);flag = false; // 主線程修改flag}
}
六、面試總結(jié)
-
必考知識點
-
volatile的作用與原理
-
synchronized的底層實現(xiàn)(Monitor、鎖升級)
-
Happens-Before原則的規(guī)則
-
指令重排序與內(nèi)存屏障
-
-
加分回答
-
結(jié)合JMM分析ConcurrentHashMap的線程安全設計。
-
對比JMM與物理內(nèi)存模型(CPU緩存、MESI協(xié)議)。
-
掌握JMM是寫出高并發(fā)代碼的基礎,也是大廠面試的必考領域!