Java高频面试之并发编程-12
hello啊,各位观众姥爷们!!!本baby今天又来报道了!哈哈哈哈哈嗝🐶
面试官:详细说说java的内存模型
Java内存模型(Java Memory Model, JMM)是Java多线程编程的核心,定义了线程如何与主内存及彼此之间交互,以确保可见性、有序性和原子性。
1. 核心组成
- 主内存(Main Memory):所有线程共享的内存区域,存储所有变量(实例字段、静态字段等)。
- 工作内存(Working Memory):每个线程私有的内存区域,保存该线程使用变量的副本。线程对变量的操作在工作内存中进行,之后同步到主内存。
2. 三大特性
-
原子性(Atomicity)
除long
和double
外的基本类型读写是原子的(但64位变量在32位JVM中可能非原子)。复合操作(如i++
)需通过synchronized
或原子类(如AtomicInteger
)保证原子性。 -
可见性(Visibility)
线程对变量的修改需同步到主内存,其他线程才能看到。未同步时,修改可能对其他线程不可见。通过volatile
、synchronized
或final
确保可见性。 -
有序性(Ordering)
编译器和处理器可能重排序指令以优化性能。JMM通过happens-before规则禁止某些重排序,确保操作顺序符合预期。
3. Happens-Before原则
定义操作间的偏序关系,确保前一个操作的结果对后续操作可见:
- 程序顺序规则:线程内操作按代码顺序执行。
- volatile规则:
volatile
变量的写操作先于后续读操作。 - 锁规则:解锁操作先于后续加锁操作。
- 线程启动规则:
Thread.start()
调用先于线程内任何操作。 - 传递性:若A先于B,B先于C,则A先于C。
4. 同步机制
-
volatile
关键字- 保证变量的可见性:写操作立即刷新到主内存,读操作直接从主内存读取。
- 禁止指令重排序:通过插入内存屏障(如
StoreStore
、LoadLoad
)限制编译器优化。 - 不保证原子性,适用于单写多读场景(如状态标志)。
-
synchronized
关键字- 进入同步块时,清空工作内存,从主内存读取变量。
- 退出时,将变量写回主内存。
- 保证原子性、可见性,并通过互斥锁防止竞态条件。
-
final
关键字- 若对象正确构造(无
this
逃逸),final
字段的初始化值对所有线程可见,无需同步。
- 若对象正确构造(无
5. 内存屏障与指令重排序
- 内存屏障(Memory Barrier)
CPU指令,分为读屏障(Load Barrier)和写屏障(Store Barrier),用于禁止重排序和强制刷新内存。 - JMM实现
通过插入内存屏障(如volatile
写后插入StoreLoad
屏障)实现happens-before规则。
6. 实际应用示例
-
双重检查锁定(Double-Checked Locking)
单例模式中需对实例变量用volatile
修饰,防止因指令重排序返回未初始化的对象。public class Singleton {private static volatile Singleton instance;public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;} }
-
线程通信(
wait()
/notify()
)
需在synchronized
块中使用,确保修改后的变量状态对其他线程可见。
7. JMM与硬件内存模型
- 抽象与实现
JMM是高级抽象,屏蔽了不同硬件(如x86的强内存模型、ARM的弱内存模型)的差异。编译器通过插入内存屏障指令,在底层适配JMM规则。