总结
就写到这了,也算是给这段时间的面试做一个总结,查漏补缺,祝自己好运吧,也希望正在求职或者打算跳槽的 程序员看到这个文章能有一点点帮助或收获,我就心满意足了。多思考,多问为什么。希望小伙伴们早点收到满意的offer! 越努力越幸运!
金九银十已经过了,就目前国内的面试模式来讲,在面试前积极的准备面试,复习整个 Java 知识体系将变得非常重要,可以很负责任的说一句,复习准备的是否充分,将直接影响你入职的成功率。但很多小伙伴却苦于没有合适的资料来回顾整个 Java 知识体系,或者有的小伙伴可能都不知道该从哪里开始复习。我偶然得到一份整理的资料,不论是从整个 Java 知识体系,还是从面试的角度来看,都是一份含技术量很高的资料。
03_java内存模型.png
为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存(L1、L2、或其他)后再进行操作,但是操作完不知道何时会写到内存。如果对发声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是写回到主内存,如果其他处理缓存的值还是旧的,再执行计算操作就会有问题。所以,在多处理器下,为了保证各个处理的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理对这个数据进行修改操作时,会重新从系统主内存中把数据读到处理器缓存里。
read 从主内丰读取
load 将主内存读取到的值写入工作内存
use 从工作内存读取数据来计算
assign 将计算好的值重新赋值到工作内存中
store 将工作内存数据写入主内存
write 将store过去的变量值赋值给主内存中的变量
复制代码
三、volatile的内存语义
只要是volatile变量,对该变量的读/写就具有原子性,如果是多个volatile操作或类似于volatile++这种复合操作,这些操作整体上不具有原子性。
3.1、volatile变量自身具有下列特性
1)可见性:对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入;
2)原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性;
3)有序性:指令重排序,编译器和指令器有时为了提高代码执行效率,会将指令重排序,要遵守一定的规则,happens-before原则,只要符合happens-before的原则,那么就不能重排,如果不符合这些规则的话,那就可以重排序
3.2、volatile写-读的内存语义
volatile写的内存语义:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。\
image.png
线程A在写flag变量后,本地内存A中被线程A更新过的两个共享变量的值被刷新到主内存中,此时,本地内存A和主内存中的共享变量的值是一致的。
volatile读的内存语义:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。\
image.png
在读flag变量后,本地内存B包含的值已经被置为无效,此时,线程B必须从主内存中读取共享变量,线程B的读取操作将导致本地内存B与主内存中共享变量的值变成一致。
总结:
1)线程A写一个volatile变量,实质上是线程A向接下来将要读这个volatile变量的某个线程发出了(其对共享变量所做修改的)消息;
2)线程B读一个volatile变量,实质上是线程B接收了之前某个线程发出的(在写这个volatile)变量之前对共享变量所做修改的)消息;
3)线程A写一个volatile变量,随后线程B读这个volatile变量,这个过程实质上是线程A通过主内存向线程B发送消息;
3.3、volatile内存语义的实现
重排序分为编译器重排序和处理器重排序。为了实现volatile内存语义,JMM会分别限制这两种类型的重排序类型。
为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。为此,基于保守策略的JMM内存屏障插入策略。
1)、在每个volatile写操作的前面插入一个StoreStore屏障;
2)、在每个volatile写操作的后面插入一个StoreLoad屏障;
3)、在每个volatile读操作的后面插入一个LoadLoad屏障;
4)、在每个volatile读操作的后面插入一个LoadStore屏障;
volatile写插入内存屏障后生成的指令序列示意图:\
image.png
StoreStore屏障可以保证在volatile写之前,其前面的所有普通写操作已经对任意处理歌可见了,因为StoreStore屏障将保障上面所有的普通写在volatile写之前刷新到主内存。
StoreLoad屏障可以避免volatile写与后面可能有的volatile读/写操作重排序。JMM在采取了保守策略:在每个volatile写的后面,或者在每个volatile读的前面插入一个StoreLoad屏障。
volatile读插入内存屏障后生成的指令序列示意图:\
最后
现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**
ctKnpCKa-1714845357941)]