news 2026/5/14 20:11:06

面试总被问 Java内存模型和 volatile,为什么总答不到点子上?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
面试总被问 Java内存模型和 volatile,为什么总答不到点子上?

前言

上周在做多线程业务汇总功能开发时,我心中产生了两个疑问:

  1. 多线程之间如何实现通信?也就是线程间依靠什么机制进行数据交换。
  2. 多线程之间如何实现同步?也就是如何管控不同线程间任务执行的先后顺序。

查资料后了解,线程间主流的通信机制分为两种:共享内存消息传递而 Java 采用的正是共享内存模型


一、Java内存模型

Java 线程间的通信由Java 内存模型(Java Memory Model,JMM)统一控制,JMM 的核心作用是定义一个线程对共享变量的写入操作,何时对其他线程可见

1、实现原理

2、主内存和本地内存区别

JMM 规范了线程与主内存之间的抽象交互关系:

  • 所有线程共享的变量都会存储在主内存中;
  • 而每个线程都拥有独立的本地内存,用于存放共享变量的工作副本。

2.1、主内存(JMM 抽象)

  • 对应硬件:计算机物理内存(RAM 内存条)为主
  • 可以近似理解:JMM 主内存 ≈ 硬件物理内存
  • 存放所有共享变量,是所有线程共享的区域。

2.2、本地内存(JMM 抽象)

完全不是一块独立的物理内存,它是JMM 虚构的抽象合集,包含了硬件里这些东西:

  • CPU 高速缓存(L1、L2、L3 缓存)
  • CPU 寄存器
  • 硬件写缓冲区
  • 编译器指令重排序、CPU 乱序执行优化
⚠️需要注意
✔️本地内存是 JMM 的抽象概念,并非物理真实存在,它涵盖了 CPU 缓存、硬件写缓冲区、CPU 寄存器,以及各类硬件和编译器的优化机制。
✔️按照 JMM 的严格规定,线程对共享变量的所有读写操作,必须在自身的本地内存中完成,不能直接读写主内存

3、线程之间如何通信?

结合1中的图可以看出,线程 A 与线程 B 若要实现通信,必须经过两个核心步骤:

  1. 线程 A 将自身本地内存中已更新的共享变量,刷新同步至主内存
  2. 线程 B 从主内存中,读取线程 A 已更新后的共享变量数据
⚠️注意
✔️线程之间无法直接访问彼此的本地内存,线程通信必须经由主内存中转
✔️JMM 正是通过规范主内存与各线程本地内存之间的数据交互规则,为 Java 程序提供内存可见性保障

二、主内存与工作内存

1、交互协议实现流程

⚠️注意
✔️读入主内存变量lockreadload
✔️线程内使用 / 修改:use → assign
✔️写回主内存:store → write → unlock

2、八种原子操作

主内存与线程本地内存间的数据交互,具体如下:

  • lock(锁定):作用于主内存变量,将变量标记为当前线程独占状态。
  • unlock(解锁):作用于主内存变量,释放已处于锁定状态的变量,释放后其他线程才可对其加锁。
  • read(读取):作用于主内存变量,将变量值从主内存传输到线程工作内存,供后续 load 操作使用。
  • load(载入):作用于工作内存变量,把 read 从主内存读取到的值,存入工作内存的变量副本中。
  • use(使用):作用于工作内存变量,将工作内存中的变量值传递给虚拟机执行引擎;只要虚拟机遇到需要读取变量值的字节码指令,就会触发该操作。
  • assign(赋值):作用于工作内存变量,把执行引擎运算后得到的值,赋值给工作内存中的变量;虚拟机遇到变量赋值类字节码指令时,便会执行此操作。
  • store(存储):作用于工作内存变量,将工作内存的变量值传输到主内存,供后续 write 操作使用。
  • write(写入):作用于主内存变量,把 store 从工作内存传出的值,最终写入更新到主内存的变量中。

三、锁的可见性原理

1、锁的获取与释放

  • 获取锁时:JMM 会将当前线程的本地内存置为无效,强制线程从主内存读取共享变量的最新值。
  • 释放锁时:JMM 会将当前线程本地内存中修改过的共享变量,强制刷新回主内存
⚠️Synchronized关键字依托这一内存原理,实现了多线程访问共享资源时的互斥性与可见性
✔️在获取锁前,线程会从主内存加载最新数据;
✔️释放锁时,线程会将修改后的数据同步回主内存,确保其他线程能看到最新值。

四、volatile可见性原理

1、volatile 读写

  • volatile 写:当线程写入一个volatile变量时,JMM 会强制将该线程本地内存中的变量值,直接刷新到主内存。
  • volatile 读:当线程读取一个volatile变量时,JMM 会将该线程对应的本地内存置为无效,强制线程从主内存中读取变量的最新值。
⚠️注意
✔️读流程:read → load → use
✔️写流程:assignstorewrite
✔️不会显式触发lock/unlock这两个操作。

2、volatile不显式触发 lock/unlock怎么保持数据一致性呢?

为实现 volatile写刷新主存、读清空本地缓存、禁止指令重排序的内存语义,JVM 会在机器码中插入内存屏障
在 x86 平台下,写入volatile变量时,会生成带有lock前缀的汇编指令,例如lock addl

该硬件lock基于总线锁和缓存一致性协议实现和 JMM 的 lock 原子操作不是一回事

它有三个核心作用:

  • 强制将写缓冲区、CPU 缓存中的数据立刻刷新到主内存;
  • 禁止指令重排序;
  • 触发缓存一致性协议,使其他 CPU 缓存副本失效,保障可见性。

3、volatile为什么禁止指令重排序?

如果不禁止,就算能刷新内存,如果允许指令重排序,代码执行顺序乱了,业务逻辑就直接出错了。

a = 1; volatile flag = true;

如果允许指令重排序,CPU 可能擅自把顺序改成:

volatile flag = true; // 先执行这行 a = 1; // 后执行这行

别的线程一看到flag=true,以为a=1已经完成,但实际上a还没赋值,直接拿到脏数据逻辑错乱

⚠️注意
✔️刷新内存只能保证别人能看到最新值
✔️禁止重排序是保证代码按你写的顺序执行,不乱跳、不颠倒逻辑

4、volatile和 synchronized在内存模型的区别

4.1、synchronized
阻塞其他线程,拿不到锁就阻塞排队,有竞态、有上下文切换。

⚠️举个例子
✔️好比进房间办事:一个人进去办 5 分钟,外面所有人原地排队、挂机等待,啥也干不了。

4.2、volatile + 硬件 lock 前缀
硬件层面串行化,只是一瞬间锁定总线 / 缓存行做内存同步
不会阻塞其他线程,所有线程依旧可以同时跑,只是内存数据立刻可见、顺序不乱

⚠️硬件 lock(volatile)
✔️锁的是内存总线一瞬间,指令干完马上释放,线程不阻塞、不挂起,继续跑
✔️volatile 的 lock好比过独木桥:每个人一秒快速冲过去,桥瞬间被占,但过完马上空出来,后面人不用排队睡觉,只是稍微等一下下继续走。

五、面试题

1、什么是Java 内存模型(JMM)?

面试里一旦被问到这个问题,很多小伙伴很容易把Java 内存模型(JMM)Java 内存结构搞混。一答题就跑偏到堆、虚拟机栈、GC 垃圾回收这些内容上,最后答得和面试官真正想问的完全不是一回事。

其实面试中问到 Java 内存模型,根本不是考内存分区,主要考察的都是多线程、Java 并发相关的知识点。

2、volatile为什么不会重排序、不会被插队?

因为加了 lock(lock addl )前缀后:

  • 硬件把这整条指令的所有内部微操作,封装成一个不可分割的原子单元
  • 硬件层面串行化,全程独占总线 / 缓存行,做完才放行
  • 别的 CPU 只能等这一条指令硬件执行完,但只是硬件内存层面短暂等待
  • 不是 Java 线程被挂起、阻塞、进队列

六、总结

JMM 定义了线程与主内存的数据交互规范,依靠八大原子操作完成变量读写同步。

synchronized 通过加锁、释放锁,既能保证线程互斥,又能刷新内存、保障可见性。

volatile 借助内存屏障禁止指令重排序,写变量强制刷新到主存,读变量清空本地缓存;底层硬件 lock 只是瞬时锁定总线做缓存同步,和 JMM 的 lock 原子操作并非同一概念,它只实现可见性和有序性,不会阻塞线程,也无法保证自增这类复合操作的原子性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/14 20:11:06

QT5之布局操作

目录 实验之前的前提 局部布局和整体布局定义 快捷工具 水平和垂直布局 水平布局 在对象区域可以看出三个已经被水平布局在一起 在对象区域选中布局,点击工具取消当前布局 可以将两个小局部进行大局部布局 网格布局 弹簧布局 分割器布局 器件对齐边距 也…

作者头像 李华
网站建设 2026/5/14 20:11:04

免费解锁百度网盘限速:Python直链解析终极指南

免费解锁百度网盘限速:Python直链解析终极指南 【免费下载链接】baidu-wangpan-parse 获取百度网盘分享文件的下载地址 项目地址: https://gitcode.com/gh_mirrors/ba/baidu-wangpan-parse 还在为百度网盘的蜗牛下载速度而烦恼吗?当你急需下载重要…

作者头像 李华
网站建设 2026/5/14 20:05:57

Vue3 +TypeScript 项目总结

前端项目记录 文章目录前端项目记录一、Html Css1. css 属性继承问题2. 居中问题3. 块级元素、行内快级元素、行内元素4. 定位 position5. 弹性布局 fiex6. 子项分组布局7. Grid 布局1. Grid 容器属性2. Grid 子项属性8. 毛玻璃无效问题8. 滑动框问题二、Vue31. 数据绑定1. 动…

作者头像 李华
网站建设 2026/5/14 20:05:23

Sublime Text AI插件深度评测:Pieces如何重塑开发工作流

1. 插件概述与核心价值如果你和我一样,是个重度 Sublime Text 用户,同时又对各种 AI 辅助工具抱有极大的热情,那么你很可能已经厌倦了在浏览器、聊天窗口和编辑器之间反复横跳的割裂感。代码片段散落在各个角落,调试时灵光一现的思…

作者头像 李华
网站建设 2026/5/14 20:05:06

如何在3分钟内轻松获取百度网盘提取码?baidupankey工具全解析

如何在3分钟内轻松获取百度网盘提取码?baidupankey工具全解析 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 你是否经常遇到这样的情况:找到了心仪的百度网盘资源,却因为不知道提取码而无法…

作者头像 李华