【面试题】你知道指令重排以及happens-before原则是什么吗?

happens-before原则,一定程度上避免指令重排

编译器、指令器可能对代码重排序、乱排、要守一定的规则,happens-before原则。只要符合happens-before的原则,那么就不能胡乱重排序,如果不符合这个规则,那么就可以自己排序。

happens-before八大原则

  • 单线程happen-before原则:在同一个线程中,书写在前面的操作happen-before后面的操作。
  • 锁的happen-before原则:同一个锁的unlock操作happen-before此锁的lock操作。
  • volatile的happen-before原则:对一个volatile变量的写操作happen-before对此变量的任意操作(当然也包括写操作了)。
  • happen-before的传递性原则:如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。
  • 线程启动的happen-before原则:同一个线程的start方法happen-before此线程的其它方法。
  • 线程中断的happen-before原则:对线程interrupt方法的调用happen-before被中断线程的检测到中断发送的代码。
  • 线程终结的happen-before原则:线程中的所有操作都happen-before线程的终止检测。
  • 对象创建的happen-before原则:一个对象的初始化完成先于他的finalize方法调用。

有过有面试官问happens-before原则,不是说要把8条背出来。而是说,规则制定了在一些特殊情况下,不允许编译器、指令器对你写的代码进行指令重排,必须保证你的代码的有序性

但是如果没有满足上面的规则,那么就可能会出现指令重排。这8条原则是避免出现指令重排的情况,要求这几个重要的场景下,是按照顺序来,但是8条规则之外,可以随意重排指令。

flag = false;
//线程1
prepare();//准备资源
flag = true;


//线程2
while(flag){
    Thread.sleep(1000);
}

execute();//基于准备好的资源执行操作

比如说这个例子,如果用volatile来修饰flag变量,一定可以让prepare()指令在flag=true之前执行这就禁止了指令重排。因为volatile要求的是,volatile前面的代码一定不能指令重拍到其修饰的变量操作后面,volatile后面的代码也不能指令重排到volatile前面。

(1)知道指令重排是什么意思

(2)知道happen-before原则

(3)volatile避免指令重排

【评论区】

1、一个线程内的所有代码是按顺序执行的(单线程中的指令重排,不影响结果)。只有多线程中,才存在指令重排的问题(多线程问题本质上是共享变量的问题)

<think>好的,用户需要查找与Java内存模型(JMM)相关的面试题整理。首先,我需要回忆JMM的核心概念,确保覆盖原子性、可见性、有序性这些关键技术点,如引用[3]提到的。然后,结合常见的面试问题,比如JMM与Java运行时内存区域的区别,这点在引用[1]中已经指出用户容易混淆的地方,必须作为重点问题列出。 接下来,happens-before原则是JMM中的重要部分,引用[4]提到了write操作和happens-before,需要详细解释其规则和应用场景。另外,工作内存和主内存的交互操作如store、load等,也是面试常考点,引用[2]和[4]都有涉及,需要整理相关题目。 还要考虑JMM如何保证可见性、有序性,可能涉及volatile关键字和内存屏障。此外,用户可能关心JMM的实际应用,比如多线程编程中的问题解决,需要结合工具类如JUC中的内容,引用[2]提到JUC工具类的特性,可以作为相关问题之一。 需要注意问题之间的逻辑顺序,从基础概念到深入原理,再扩展到实际应用和常见误区。同时,确保每个问题都有对应的知识点支撑,引用正确的文献来源,例如原子性对应引用[3],happens-before对应引用[4]等。最后,验证问题是否全面,覆盖JMM的主要方面,帮助用户系统准备面试。</think>### Java内存模型(JMM)面试题整理 #### 1. **什么是Java内存模型(JMM)?与Java运行时内存区域有何区别?** - **JMM**:定义多线程环境中线程如何与主内存和工作内存交互,确保原子性、可见性、有序性[^3][^4]。 - **区别**:JMM是抽象规范,关注多线程通信;运行时内存区域(堆、栈等)是物理内存划分[^1]。 #### 2. **JMM的三大特性是什么?如何实现?** - **原子性**:通过`synchronized`、`Lock`或原子类(如`AtomicInteger`)保证操作不可分割。 - **可见性**:通过`volatile`、`synchronized`或`final`确保线程修改对其他线程立即可见[^2][^4]。 - **有序性**:通过`volatile`禁止指令重排、`synchronized`或`happens-before`原则约束执行顺序[^3]。 #### 3. **什么是happens-before原则?列举具体规则。** - **定义**:保证前一个操作的结果对后一个操作可见的规则。 - **规则**: 1. 程序顺序规则:同一线程中的操作按代码顺序执行。 2. `volatile`规则:对`volatile`变量的写操作先于后续的读操作。 3. 锁规则:解锁操作先于后续的加锁操作。 4. 传递性:若A先于B,B先于C,则A先于C。 #### 4. **volatile关键字的作用和底层实现原理?** - **作用**: 1. 保证变量对所有线程的可见性(写操作立即刷新到主内存)。 2. 禁止指令重排序优化。 - **原理**:通过插入内存屏障(Memory Barrier)实现。 #### 5. **什么是工作内存和主内存?如何交互?** - **主内存**:所有线程共享的内存区域,存储变量原始值。 - **工作内存**:线程私有的内存副本,存储变量的临时拷贝。 - **交互操作**:`read`/`load`(读取到工作内存)、`use`(使用)、`assign`(赋值)、`store`/`write`(写回主内存)。 #### 6. **JMM如何解决多线程并发问题?举例说明。** - **可见性问题**:使用`volatile`修饰变量,如状态标记位。 - **原子性问题**:使用`synchronized`或`ReentrantLock`保护临界区。 - **有序性问题**:通过`happens-before`原则约束操作顺序。 #### 7. **双重检查锁(DCL)单例模式为何需要volatile?** - **原因**:防止指令重排序导致对象未初始化完成就被其他线程访问。 - **示例**: ```java 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; } } ``` #### 8. **JMM与JUC工具类(如AtomicInteger)的关系?** - **JUC工具类**:基于CAS(Compare-And-Swap)和`volatile`实现原子操作,符合JMM的原子性和可见性要求。 --- ### 相关问题扩展 1. 如何用`volatile`和`synchronized`分别保证可见性? 2. CAS操作在JMM中如何保证原子性?存在什么问题? 3. JMM中的内存屏障类型及其作用? 4. 什么是指令重排序?JMM如何限制它? 5. 如何通过JMM解释线程安全的延迟初始化? --- 以上问题覆盖JMM的核心概念、实现原理和实际应用场景,适用于中高级Java面试准备[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值