JMM面经

发布于:2025-05-23 ⋅ 阅读:(23) ⋅ 点赞:(0)

1.你说一下什么是JMM?里面有什么规定限制?

答:

  • 是一个抽象的概念,用于为java虚拟机规范中多线程共享内存制定的一套规则和定义;
  • 主要解决原子性,可见性,有序性三大并发的问题

出现背景:简答来说就是同一个程序在不同的硬件环境或操作系统环境内存的访问都是有差异的,然后就是使用这个JMM规范,他能够很好的屏蔽掉这些差异,从而保证java程序在不同的环境下达到一致的效果;

规范:

  • 多线程之间如何读写共享变量
    • 所有共享变量都存储在主内存中(heap)。
    • 每个线程有自己的工作内存,里面保存了主内存中共享变量的副本。
    • 线程对变量的所有操作(读写)必须在工作内存中进行,不能直接操作主内存。
    • 不同线程之间无法直接访问对方的工作内存
  • 如何保证操作的原子性?
    • synchronized关键字。因此在 synchronized 块之间的操作都是原子性的
  • 什么时候变量对其他线程可见?
    • 使用 volatile 可以保证写操作后,当变量被volatile修饰时,这个变量被修改后会立刻刷新到主内存,当其它线程需要读取该变量时,会去主内存中读取新值。
    • 使用 synchronized 可通过内存屏障,保证进入/退出临界区时刷新主内存。
    • final : final修饰的字段,一旦初始化完成,如果没有对象逸出(指对象为初始化完成就可以被别的线程使用),那么对于其他线程都是可见的。
  • 哪些操作可以保证有序性?
    • synchronized或者volatile保证多线程之间操作的有序性?

多线程面临的两个问题线程之间的通信和线程之间的同步,这两个问题如果仔细分析,从结果的角度看线程之间的通信就是可见性问题,线程之间的同步就是原子性和有序性的问题。

2. 这个有序性操作的实现原理有什么?

答:

  • happens-before 原则: happens-before原则是Java内存模型中定义的两项操作之间的偏序关系,如果操作A先行发生于操作B,也就是说发生操作B之前,操作A产生的影响能被操作B观察到。这里的“影响”包括修改共享变量,方法调用。
  • synchronized 机制: synchronized能够保证有序性是因为synchronized可以保证同一时间只有一个线程访问代码块,而单线程环境下,JMM能够保证代码的串行语义;虽然使用synchronized的代码块,还可以发生指令重排序,但是synchronized可以保证只有一个线程执行,所以最后的结果还是正确的。
  • volatile 机制: volatile的底层是使用内存屏障来保障有序性的。
    • 写volatile变量时,可以确保volatile写之前的操作不会被编译器重排序到volatile写之后。
    • 读volatile变量时,可以确保volatile读之后的操作不会被编译器重排序到volatile读之前。

3. 指令重排序是什么? 内存屏障又是什么?

答:

  • 指令重排序:为了使指令更加符合CPU的执行特性,最大限度的发挥机器的性能,提高程序的执行效率,只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码逻辑顺序不一致
  • 内存屏障是一种 CPU 指令,用于控制内存操作的顺序性
    • 强制刷新缓存数据到主内存
    • 强制从主内存读取最新数据
    • 阻止指令重排序
  • 禁止指令重排序的核心:在关键代码位置通过 JVM 插入“内存屏障指令(memory barrier)”,控制编译器和 CPU 的重排行为,从而保障多线程并发执行的可见性、有序性和正确性

4. threadlocal是什么?应用范围场景是什么,说一下。

答:

  • ThreadLocal 是 Java 提供的一种线程本地变量工具,它可以为每个线程维护一份独立的变量副本,互不影响,常用于数据库连接、用户信息、日志上下文等线程隔离场景。它避免了多线程共享变量带来的并发问题,是线程安全的一种实现方式
  • 优势:不需要加锁,避免共享导致的并发问题
  • 缺点:可能会导致内存泄漏,使用后要及时进行remove

使用:

  • 通常使用get()、set()、remove()私有变量处理;
  • 一次只能设置一个值,如果要多个私有变量就设置多个threadlocal。
ThreadLocal<String> userIdLocal = new ThreadLocal<>();
ThreadLocal<String> tokenLocal = new ThreadLocal<>();
ThreadLocal<Date> loginTimeLocal = new ThreadLocal<>();

5.你说这个线程的工作内存里面存放着主内存变量的副本,那与threadLocal有什么区别,这个存储的不也是线程独有变量?

答:

  • 这本质不一样,一个JVM规范一种,用于保证多线程操作时共享变量的内存可见性和有序性,是不可控和不可见的;而threadlocal是类库提供的一个工具,用于为每个线程独立地存储变量,不共享,并且这个是自己操作的,是可控的。

6.这个threadlocal与这个cocurrentmap的区别是什么?

答:

  • ThreadLocal是线程私有变量,不共享内存,都是线程内存独立存在;
  • cocurrentmap是一个集合,所有线程共享这个数据结构。

网站公告

今日签到

点亮在社区的每一天
去签到