面试官:我问的是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内存模型中。



相关文章
|
2天前
|
存储 Java 开发者
深入理解Java虚拟机:JVM内存模型解析
【5月更文挑战第27天】 在Java程序的运行过程中,JVM(Java Virtual Machine)扮演着至关重要的角色。作为Java语言的核心执行环境,JVM不仅负责代码的执行,还管理着程序运行时的内存分配与回收。本文将深入探讨JVM的内存模型,包括其结构、各部分的作用以及它们之间的相互关系。通过对JVM内存模型的剖析,我们能够更好地理解Java程序的性能特征,并针对性地进行调优,从而提升应用的执行效率和稳定性。
|
13天前
|
存储 算法 Java
Java一分钟之-Java内存模型与垃圾回收机制概览
【5月更文挑战第16天】本文简述Java内存模型(JMM)和垃圾回收(GC)机制。JMM包括栈、堆、方法区、程序计数器和本地方法栈。GC负责回收不再使用的对象内存,常用算法有新生代、老年代和全堆GC。文章讨论了内存溢出、死锁和GC性能等问题,提出了解决方案,如调整JVM参数和优化GC策略。此外,还强调了避免内存泄漏、大对象管理及正确释放资源的重要性。理解这些概念有助于提升Java应用的性能和稳定性。
25 1
|
14天前
|
存储 NoSQL 算法
Redis源码、面试指南(2)内存编码数据结构(下)
Redis源码、面试指南(2)内存编码数据结构
27 4
|
14天前
|
存储 NoSQL API
Redis源码、面试指南(2)内存编码数据结构(上)
Redis源码、面试指南(2)内存编码数据结构
30 0
|
14天前
|
存储 Java
面试官:素有Java锁王称号的‘StampedLock’你知道吗?我:这什么鬼?
【5月更文挑战第9天】面试官:素有Java锁王称号的‘StampedLock’你知道吗?我:这什么鬼?
54 24
|
14天前
|
存储 Java
深入理解Java虚拟机:JVM内存模型
【4月更文挑战第30天】本文将详细解析Java虚拟机(JVM)的内存模型,包括堆、栈、方法区等部分,并探讨它们在Java程序运行过程中的作用。通过对JVM内存模型的深入理解,可以帮助我们更好地编写高效的Java代码,避免内存溢出等问题。
|
14天前
|
Java 开发者
【JAVA】Java内存模型中的happen-before
Happen-before关系帮助开发者理解多线程程序中操作的执行顺序和可见性,从而避免竞态条件和数据不一致性问题。它提供了一种可预测的规则来确保线程之间的正确交互。
32 0
|
14天前
|
存储 缓存 安全
深入理解Java内存模型及其作用
深入理解Java内存模型及其作用
|
14天前
|
存储 缓存 Java
[JVM] 浅谈JMM(Java 内存模型)
[JVM] 浅谈JMM(Java 内存模型)
|
22小时前
|
缓存 Java 编译器
Java多线程基础-6:线程安全问题及解决措施,synchronized关键字与volatile关键字(二)
加锁其实是一个比较低效的操作,因为加锁就可能涉及到阻塞等待。
12 1