深入浅出Java内存模型
面试官:我记得上一次已经问过了为什么要有Java内存模型
面试官:我记得你的最终答案是:Java为了屏蔽硬件和操作系统访问内存的各种差异,提出了「Java内存模型」的规范,保证了Java程序在各种平台下对内存的访问都能得到一致效果
候选者:嗯,对的
面试官:要不,你今天再来讲讲Java内存模型这里边的内容呗?
候选者:嗯,在讲之前还是得强调下:Java内存模型它是一种「规范」,Java虚拟机会实现这个规范。
候选者:Java内存模型主要的内容,我个人觉得有以下几块吧
候选者:1. Java内存模型的抽象结构
候选者:2. happen-before规则
候选者:3.对volatile内存语义的探讨(这块我后面再好好解释)

面试官:那要不你就从第一点开始呗?先聊下Java内存模型的抽象结构?
候选者:嗯。Java内存模型定义了:Java线程对内存数据进行交互的规范。
候选者:线程之间的「共享变量」存储在「主内存」中,每个线程都有自己私有的「本地内存」,「本地内存」存储了该线程以读/写共享变量的副本。
候选者:本地内存是Java内存模型的抽象概念,并不是真实存在的。
候选者:顺便画个图吧,看完图就懂了。

候选者:Java内存模型规定了:线程对变量的所有操作都必须在「本地内存」进行,「不能直接读写主内存」的变量
候选者:Java内存模型定义了8种操作来完成「变量如何从主内存到本地内存,以及变量如何从本地内存到主内存」
候选者:分别是read/load/use/assign/store/write/lock/unlock操作
候选者:看着8个操作很多,对变量的一次读写就涵盖了这些操作了,我再画个图给你讲讲

候选者:懂了吧?无非就是读写用到了各个操作(:
面试官:懂了,很简单,接下来说什么是happen-before吧?
候选者:嗯,好的(:
候选者:按我的理解下,happen-before实际上也是一套「规则」。Java内存模型定义了这套规则,目的是为了阐述「操作之间」的内存「可见性」
候选者:从上次讲述「指令重排」就提到了,在CPU和编译器层面上都有指令重排的问题。
候选者:指令重排虽然是能提高运行的效率,但在并发编程中,我们在兼顾「效率」的前提下,还希望「程序结果」能由我们掌控的。
候选者:说白了就是:在某些重要的场景下,这一组操作都不能进行重排序,「前面一个操作的结果对后续操作必须是可见的」。

面试官:嗯...
候选者:于是,Java内存模型就提出了happen-before这套规则,规则总共有8条
候选者:比如传递性、volatile变量规则、程序顺序规则、监视器锁的规则...(具体看规则的含义就好了,这块不难)
候选者:只要记住,有了happen-before这些规则。我们写的代码只要在这些规则下,前一个操作的结果对后续操作是可见的,是不会发生重排序的。
面试官:我明白你的意思了
面试官:那最后说下volatile?
候选者:嗯,volatile是Java的一个关键字
候选者:为什么讲Java内存模型往往就会讲到volatile这个关键字呢,我觉得主要是它的特性:可见性和有序性(禁止重排序)
候选者:Java内存模型这个规范,很大程度下就是为了解决可见性和有序性的问题。

面试官:那你来讲讲它的原理吧,volatile这个关键字是怎么做到可见性和有序性的
候选者:Java内存模型为了实现volatile有序性和可见性,定义了4种内存屏障的「规范」,分别是LoadLoad/LoadStore/StoreLoad/StoreStore
候选者:回到volatile上,说白了,就是在volatile「前后」加上「内存屏障」,使得编译器和CPU无法进行重排序,致使有序,并且写volatile变量对其他线程可见。
候选者:Java内存模型定义了规范,那Java虚拟机就得实现啊,是不是?
面试官:嗯...
候选者:之前看过Hotspot虚拟机的实现,在「汇编」层面上实际是通过Lock前缀指令来实现的,而不是各种fence指令(主要原因就是简便。因为大部分平台都支持lock指令,而fence指令是x86平台的)。
候选者:lock指令能保证:禁止CPU和编译器的重排序(保证了有序性)、保证CPU写核心的指令可以立即生效且其他核心的缓存数据失效(保证了可见性)。

面试官:那你提到这了,我想问问volatile和MESI协议是啥关系?
候选者:它们没有直接的关联。
候选者:Java内存模型关注的是编程语言层面上,它是高维度的抽象。MESI是CPU缓存一致性协议,不同的CPU架构都不一样,可能有的CPU压根就没用MESI协议...
候选者:只不过MESI名声大,大家就都拿他来举例子了。而MESI可能只是在「特定的场景下」为实现volatile的可见性/有序性而使用到的一部分罢了(:
面试官:嗯...
候选者:为了让Java程序员屏蔽上面这些底层知识,快速地入门使用volatile变量
候选者:Java内存模型的happen-before规则中就有对volatile变量规则的定义
候选者:这条规则的内容其实就是:对一个 volatile 变量的写操作相对于后续对这个 volatile 变量的读操作可见
候选者:它通过happen-before规则来规定:只要变量声明了volatile 关键字,写后再读,读必须可见写的值。(可见性、有序性)
面试官:嗯...了解了
本文总结:
为什么存在Java内存模型:Java为了屏蔽硬件和操作系统访问内存的各种差异,提出了「Java内存模型」的规范,保证了Java程序在各种平台下对内存的访问都能得到一致效果
Java内存模型抽象结构:线程之间的「共享变量」存储在「主内存」中,每个线程都有自己私有的「本地内存」,「本地内存」存储了该线程以读/写共享变量的副本。线程对变量的所有操作都必须在「本地内存」进行,而「不能直接读写主内存」的变量
happen-before规则:Java内存模型规定在某些场景下(一共8条),前面一个操作的结果对后续操作必须是可见的。这8条规则成为happen-before规则
volatile:volatile是Java的关键字,修饰的变量是可见性且有序的(不会被重排序)。可见性由happen-before规则完成,有序性由Java内存模型定义的「内存屏障」完成,实际HotSpot虚拟机实现Java内存模型规范,汇编底层通过Lock指令来实现。

欢迎关注我的微信公众号【Java3y】来聊聊Java面试,对线面试官系列持续更新中!

【对线面试官-移动端】系列 一周两篇持续更新中!
【对线面试官-电脑端】系列 一周两篇持续更新中!
原创不易!!求三连!!
深入浅出Java内存模型的更多相关文章
- 【Java并发基础】Java内存模型解决有序性和可见性
前言 解决并发编程中的可见性和有序性问题最直接的方法就是禁用CPU缓存和编译器的优化.但是,禁用这两者又会影响程序性能.于是我们要做的是按需禁用CPU缓存和编译器的优化. 如何按需禁用CPU缓存和编译 ...
- JVM学习(3)——总结Java内存模型
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 为什么学习Java的内存模式 缓存一致性问题 什么是内存模型 JMM(Java Memory Model)简 ...
- 浅析java内存模型--JMM(Java Memory Model)
在并发编程中,多个线程之间采取什么机制进行通信(信息交换),什么机制进行数据的同步? 在Java语言中,采用的是共享内存模型来实现多线程之间的信息交换和数据同步的. 线程之间通过共享程序公共的状态,通 ...
- JMM(java内存模型)
What is a memory model, anyway? In multiprocessorsystems, processors generally have one or more laye ...
- 《深入理解Java内存模型》读书总结
概要 文章是<深入理解Java内容模型>读书笔记,该书总共包括了3部分的知识. 第1部分,基本概念 包括"并发.同步.主内存.本地内存.重排序.内存屏障.happens befo ...
- Java内存模型深度解析:final--转
原文地址:http://www.codeceo.com/article/java-memory-6.html 与前面介绍的锁和Volatile相比较,对final域的读和写更像是普通的变量访问.对于f ...
- Java内存模型深度解析:volatile--转
原文地址:http://www.codeceo.com/article/java-memory-4.html Volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特 ...
- Java内存模型深度解析:顺序一致性--转
原文地址:http://www.codeceo.com/article/java-memory-3.html 数据竞争与顺序一致性保证 当程序未正确同步时,就会存在数据竞争.java内存模型规范对数据 ...
- Java内存模型深度解析:基础部分--转
原文地址:http://www.codeceo.com/article/java-memory-1.html 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何 ...
随机推荐
- JS_DOM操作之操作标签
<标签名 属性1="属性值1" 属性2="属性值2"-->文本</标签名> 1 - 文本操作 <div class="c ...
- windows 安装pip 及更换pip国内源
一.官网下载压缩包并解压. 官网:https://pypi.org/project/pip/#files 文件:选择.tar.gz版本 image 解压后,进入解压文件目录,在当前路径下打开cmd窗口 ...
- 处理burp log 小脚本
burp 日志保存 保存的日志格式为 将日志中的数据包 每个数据包保存到一个单独的txt里面 然后可以控制目录放进不同的目录中 #coding=utf-8 import re import os de ...
- RSTP
一.STP协议的缺点,存在的问题 STP 协议工作时间收敛慢,响应时间长---------->RSTP 原始的802.1d(stp)不支持多个vlan---->(PVST===>把一 ...
- 288 day05_异常,线程
day05 [异常.线程] 主要内容 异常.线程 教学目标 [ ] 能够辨别程序中异常和错误的区别 [ ] 说出异常的分类 [ ] 说出虚拟机处理异常的方式 [ ] 列举出常见的三个运行期异常 [ ] ...
- PHP中的PDO对象操作学习(一)初始化PDO及原始SQL语句操作
PDO 已经是 PHP 中操作数据库事实上的标准.包括现在的框架和各种类库,都是以 PDO 作为数据库的连接方式.基本上只有我们自己在写简单的测试代码或者小的功能时会使用 mysqli 来操作数据库. ...
- Nginx系列(10)- Nginx配置文件详解
nginx文件结构 ... #全局块 events { #events块 ... } http #http块 { ... #http全局块 server #server块 { ... #server全 ...
- phpstoem破解
https://blog.csdn.net/voke_/article/details/78794567
- jmeter设置为中文
我的jmeter安装路径是在D:\Jmeter\apache-jmeter-5.1.1\bin. 设置中文有2种方法: 1.第一种方法:点击jmeter.bat进入jmeter界面,点击[option ...
- 低差异序列 (low-discrepancy sequences)之Hammerysley在半球中采样点方法的介绍
半球上的Hammersley 源作者:Holger Dammertz 一组关于如何在2D中使用Hammersley点集以在着色器程序中快速实用地生成半球方向的笔记.如果你发现任何错误或有意见,不要犹豫 ...