java 内存模型(JMM)
java 内存模型是一种概念,它描述的是虚拟机的规则或者规范,通过规范定义程序中的变量访问方式。规范中定义:
1、所有的变量都存储在主内存中
2、每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)
3、线程对共享变量的所有操作都必须在自己的工作内存,不能直接从相互内存中读写也不能从主内存中操作
4、线程间变量值得传递需要通过主内存来完成

有了上述的模型,虚拟机实现内存结构时在这个规范中,正如上一章节中,jvm内存结构的实现一样:堆和方法区(元空间)可以看作是主内存,而虚拟机栈,本地方法栈这些线程私有的可以认为是工作内存,当然这只是一种类比,实际实现可能复杂的多。
Java内存模型是围绕并发过程中如何处理原子性、可见性和有序性这3个特征来建立的。
原子性
一个操作要么都执行要么都不执行,为了保证原子性,Java内存模型提供了8种操作指令,这些操作都是原子的(double和long类型有例外)
use:变量从工作内存传递给执行引擎。每当虚拟机线程遇到一个需要使用到变量的值的字节码指令时将会执行这个操作;
assign:把一个从执行引擎接收到的值复制给工作内存的变量。每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作;
read:把一个变量的值从主内存拷贝到工作内存,以便为随后的load动作使用;
load:把read操作从主内存获取的变量值放入工作空间的副本中;
store:把工作内存中的变量值传送到主内存中,为后续的write操作使用;
write:把store操作从工作内存得到的变量的值放入主内存的变量中;
lock:把一个变量标识为线程独占状态;
unlock:释放线程独占的变量。
这8个操作主要是为变量服务的,让变量在主内存和工作内存之间来回移动。

参考:Java 内存模型与线程
可见性
当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。提供了三个关键字保证可见性:
- volatile 当某个线程修改变量时能够立即同步到主内存,并使得其他线程使用时强制从主内存刷新该值;
- synchronized 对一个变量执行 unlock 操作之前可以先把此变量同步回主内存中;
- 被 final 修饰的字段在构造器中一旦初始化完成且构造器没有把 this 的引用传递出去,就可以在其他线程中就能看见 final 字段的值
有序性
指令按顺序执行。提供两个关键字保证有序性:
- volatile 插入内存屏障指令防止指令重排;
内存屏障有三种类型和一种伪类型(面试官可能会问volatile 如何防止指令重排的,一般我们回答插入内存屏障指令即可,但是总有人喜欢追根究底):
lfence:即读屏障(Load Barrier),在读指令前插入读屏障,可以让高速缓存中的数据失效,重新从主内存加载数据,以保证读取的是最新的数据。
sfence:即写屏障(Store Barrier),在写指令之后插入写屏障,能让写入缓存的最新数据写回到主内存,以保证写入的数据立刻对其他线程可见。
mfence,即全能屏障,具备ifence和sfence的能力。
Lock前缀:Lock不是一种内存屏障,但是它能完成类似全能型内存屏障的功能。
- synchronized 保证一个变量在同一个时刻只允许一条线程对其进行 lock 操作,使得持有同一个锁的两个同步块只能串行地进入
当面试官问我们什么是java 内存模型(JMM)时,我们可以这样回答:
答:java 内存模型是一种规范,它规定所有的变量都应该存储在主内存中,每个线程都应该有自己的工作内存,工作内存中使用的变量应该为主内存的副本,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,不同的线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。
当面试官问我们JMM中如何保证可见性时,我们可以这样回答:
答:使用关键字volatile、synchronized和final
参考:
https://www.cnblogs.com/huansky/p/15939007.html
Java内存模型描述了主内存与工作内存的交互规则,确保多线程环境下的数据一致性。它通过原子性、可见性和有序性保证并发安全。volatile关键字能保证可见性和部分有序性,synchronized确保互斥和可见性,final保证初始化后的不变性。内存屏障则在指令层面防止重排序,维持执行顺序。
2602

被折叠的 条评论
为什么被折叠?



