面试官:我问的是Java内存模型,你回答堆栈方法区干嘛?

简介: 很多人会把Java内存区域(运行时数据区)和Java内存模型(JMM)搞混,这两者是完全不一样的东西。Java内存区域是指JVM运行时数据分区域存储,而Java内存模型是定义了线程和主内存之间的抽象关系,了解Java内存模型是学好Java并发编程的基础。

微信搜《Java鱼仔》真的可以变强!!


(一)概述


很多人会把Java内存区域(运行时数据区)和Java内存模型(JMM)搞混,这两者是完全不一样的东西。


Java内存区域是指JVM运行时数据分区域存储,而Java内存模型是定义了线程和主内存之间的抽象关系,了解Java内存模型是学好Java并发编程的基础。


(二)Java内存模型


Java内存模型中规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。我们来看一张图:

网络异常,图片无法展示
|


每个线程拥有一个自己的私有工作内存,需要变量时从主内存中拷贝一份到工作内存,如果更新过变量之后再将共享变量刷新到主内存。


但是两个线程之间,是没有办法读取对方工作内存中的变量值的。看一个例子:


publicclassTest {
privatestaticbooleanflag=false;
publicstaticvoidmain(String[] args) throwsInterruptedException {
newThread(newRunnable() {
@Overridepublicvoidrun() {
System.out.println("waiting");
while (!flag){}
System.out.println("in");
            }
        }).start();
Thread.sleep(2000);
newThread(newRunnable() {
@Overridepublicvoidrun() {
System.out.println("change flag");
flag=true;
System.out.println("change success");
            }
        }).start();
    }
}

首先定义了一个静态变量flag为false,A线程等待flag等于true后输出in,于是我们新开

一个线程将flag修改为true。结果是A线程依旧无法输出in。


网络异常,图片无法展示
|


原理看Java内存模型的图就理解了,不同的线程修改变量,对本地线程是不可见的。


(三)JMM数据的原子操作


通过上面这段代码,我们已经知道了Java内存模型的结构,那么工作内存和主内存之间是如何读取变量又是如何修改变量的呢?JMM提供了对变量的一系列原子操作。我们先不讲理论,看个图,这个图描述了上面一段代码的执行过程:


网络异常,图片无法展示
|
整个过程一共十步,重复几个步骤不讲了,我把不重复的六个操作列一下:


read:从主内存读取数据


load:将主内存读取到的数据写入工作内存


use :从工作内存中读取数据来使用


assign:把计算好的值重新赋值到工作内存中


store:将工作内存数据写入主内存


write:将store过去的变量赋值给主内存中的变量


通过上面的图,对下面六个原子操作的理解应该可以更加深刻了。JMM的原子操作一共有八个,下面列出剩下的两个


lock:将主内存变量加锁,标识为线程独占状态


unlock:将主内存变量解锁,解锁后其他线程可以锁定该变量


(四)JMM缓存不一致问题


从前面的例子我们已经看到了,一个线程修改完数据,另外一个线程无法立即可见,这就是JMM缓存不一致的问题,有两种解决办法:


加锁:


还记得我们没有用到过的JMM原操作的最后两个吗,lock和unlock,使用这两个操作就可以实现缓存一致性,一个线程想要获取某个主内存变量时,先使用lock将主内存变量加锁,只有他才能使用,等用完后再unlock,其他线程才能竞争。但是加锁意味着性能低。


MESI缓存一致性协议:


这个协议涉及到cpu的总线嗅探机制,从上面的JMM执行的流程图中我们可以看到当某个线程修改了共享变量后,他会回写到主内存,MESI缓存一致性协议就是通过cpu的总线嗅探机制,将其他也正在使用该变量的线程的数据失效掉,使得这些线程要重新读取主内存中的值,从而保证缓存最终一致性。(volatile的实现原理)


(五)总结


Java内存模型在多并发中十分重要,包括后面学习volatile或者synchronized这个关键字的时候,都会回到这个Java内存模型中。



相关文章
|
10天前
|
存储 Java 编译器
Java内存区域详解
Java内存区域详解
25 0
Java内存区域详解
|
20天前
|
缓存 算法 Java
Java内存管理与调优:释放应用潜能的关键
【4月更文挑战第2天】Java内存管理关乎性能与稳定性。理解JVM内存结构,如堆和栈,是优化基础。内存泄漏是常见问题,需谨慎管理对象生命周期,并使用工具如VisualVM检测。有效字符串处理、选择合适数据结构和算法能提升效率。垃圾回收自动回收内存,但策略调整影响性能,如选择不同类型的垃圾回收器。其他优化包括调整堆大小、使用对象池和缓存。掌握这些技巧,开发者能优化应用,提升系统性能。
|
25天前
|
Java 程序员
java线程池讲解面试
java线程池讲解面试
43 1
|
17天前
|
缓存 安全 Java
Java并发编程进阶:深入理解Java内存模型
【4月更文挑战第6天】Java内存模型(JMM)是多线程编程的关键,定义了线程间共享变量读写的规则,确保数据一致性和可见性。主要包括原子性、可见性和有序性三大特性。Happens-Before原则规定操作顺序,内存屏障和锁则保障这些原则的实施。理解JMM和相关机制对于编写线程安全、高性能的Java并发程序至关重要。
|
24天前
|
缓存 Java C#
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(一)
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
66 0
|
7天前
|
存储 缓存 监控
Java内存管理:垃圾回收与内存泄漏
【4月更文挑战第16天】本文探讨了Java的内存管理机制,重点在于垃圾回收和内存泄漏。垃圾回收通过标记-清除过程回收无用对象,Java提供了多种GC类型,如Serial、Parallel、CMS和G1。内存泄漏导致内存无法释放,常见原因包括静态集合、监听器、内部类、未关闭资源和缓存。内存泄漏影响性能,可能导致应用崩溃。避免内存泄漏的策略包括代码审查、使用分析工具、合理设计和及时释放资源。理解这些原理对开发高性能Java应用至关重要。
|
14天前
|
Java 关系型数据库 MySQL
大厂面试题详解:Java抽象类与接口的概念及区别
字节跳动大厂面试题详解:Java抽象类与接口的概念及区别
39 0
|
15天前
|
存储 缓存 安全
【企业级理解】高效并发之Java内存模型
【企业级理解】高效并发之Java内存模型
|
21天前
|
Java
java中jar启动设置内存大小java -jar 设置堆栈内存大小
java中jar启动设置内存大小java -jar 设置堆栈内存大小
11 1
|
22天前
|
缓存 算法 Java
Java内存管理:优化性能和避免内存泄漏的关键技巧
综上所述,通过合适的数据结构选择、资源释放、对象复用、引用管理等技巧,可以优化Java程序的性能并避免内存泄漏问题。
25 5