2.什么是JAVA内存模型?

简介: 2.什么是JAVA内存模型?

为什么要设计JAVA内存模型?

小陈:老王,看了上一篇的《CPU多级缓存模型》,有个疑问为什么还要有JAVA内存模型啊?

老王:这么来说吧,CPU多级缓存模型,只是一个规范,但是底层基于这个规范的实现还是有很多种的。比如:

(1)基于这个模型之上不同的计算机厂商可能对这个模型底层的实现不一样,包括底层运行的指令集、有的还基于多级缓存模型之上还引入了写换从器、无效队列等。

(2)不同的操作系统windows、linux的指令集不一样。

老王:

所以JAVA语言为了应对上述的问题:不同的系统、不同的计算机厂商的底层实现不同。基于它们之上设计了一个JAVA内存模型(JMM)的规范,就是为了屏蔽底层操作系统、厂商之间的差异性。程序员运行是只需要关注JMM,而不需关注底层系统、厂商指令的差异性,这些由JMM去适配不同的系统和厂商的指令集,这样就能实现一次编写,“到处乱跑”的跨平台效果。

小陈:哦哦,原来是为了屏蔽操作系统、不同的计算机厂商的底层的差异性,实现跨平台的无差异性运行的效果啊。

JAVA内存模型是怎么样的?

小陈:原因我大概知道了,那JAVA内存模型大概是个什么样的结构啊?

老王:给你看下下面这张图,你大概就理解了

image.png

老王:上图就是JAVA内存模型的大致结构图,JAVA内存模型定义了一个规范。那就是每个线程都有自己的工作内存,线程操作共享变量的时候需要从主内存读取到自己的工作内存,然后在传递给工作线程使用,共享变量修改后先刷新到工作内存,然后再刷新回主内存;这个JAVA内存模型是基于我们上一讲CPU多级缓存模型上建立的。

JAVA内存模型定义的八种原子操作

小陈:看了上面的图,JAVA内存模型的结构我基本知道了,但是JMM这哥们怎么工作的我还是很模糊啊?

老王:没关系,我来给你讲讲,JMM定义了8种指令,它工作的时候就是通过这8种指令来操作内存的。

包括怎么锁定变量、怎么将数据从主存传到工作内存、怎么传给正在被CPU调度的线程、修改之后CPU怎么传回工作内存、工作内存又怎么传递回主存:

  1. lock(锁定):把主内存中的一个共享变量标记为一个线程独享的状态
  2. unlock(解锁):把主内存的变量从线程独享的lock状态中解除出来
  3. read(读取):把主内存的一个共享变量传输工作内存中
  4. load(载入):把从主内存传输到工作内存的共享变量,赋值给工作内存中的变量副本
  5. use(使用):把工作内存中的变量副本的值,传递给执行引擎CPU
  6. assign(赋值):执行引擎(CPU)执行完之后,把修改过的变量值重新赋值给工作内存中的变量副本
  7. store(存储):把工作内存中修改过共享变量的值传递到主内存中
  8. write(写入):把传递到主内存中的变量值,重新写回给主内存的共享变量

小陈:...., 这都啥啊...

image.png

老王:给你画张图吧,你就了解了:

比如线程A要执行一个x++的操作,可能要经历下面的过程:

image.png

小陈:牛逼啊老王....

小陈:额,感觉不对啊,上面只有六种指令,还有另外两种lock、unlock指令是咋用的?

老王:这还不简单,再给你画张图就KO了。

比如还是上面的x++操作

image.png

(1)工作线程A操作该共享内存变量的时候,执行lock指令,主内存中的这个共享变量;同时告诉线程B这个共享变量我准备修改了,让它失效掉。

(2)B线程的变量副本失效之后,运行时候用到,需要到主内存重新读取(执行read、load操作放入工作内存);发现该主内存的变量被锁定了,读取失败;此时相当于线程A拥有该变量的独享操作

(3)线程A执行i++操作,经历上述说的(read、load、use、assign、store、write)指令之后;操作完成执行unlock释放锁定的这个内存变量

(4)线程B这个时候再去主内存读取的时候,发现未被锁定,就可以重新读取了

小陈:牛逼啊老王,听君一席话,白读十年书。献上我的掌声......

image.png

多线程并发在JMM操作带来的可见性问题

小陈:JMM以及它的8中基本操作我了解了,但是我发现了个问题,在多线程操作的时候可能会有数据不一致的问题,比如我画个图讲一下我的想法:

image.png

线程A和线程B同时执行共享变量x的++操作;线程B在线程A将x=1的值刷入主内存之前读取x=0,这样就会导致数据错了。

老王:很好,看来小陈很聪明啊,理解得很快啊

小陈:嘿嘿,一般般啦...

老王:你说的这个问题,正是多线程并发操作的可见性问题。

小陈:我知道多线程并发操作的时候,会有可见性、有序性、原子性的问题,这种情况会导致可见性问题我知道了,但是有序性和原子性是什么?什么时候会发生的?会导致什么问题?

老王:哈哈,你有这个好奇心很好,我们今天先说这么多,可见性、有序性、原子性的问题我们留到下一篇再说,别急哦,一点点慢慢来,啧啧..


相关文章
|
4月前
|
安全 Java 应用服务中间件
Spring Boot + Java 21:内存减少 60%,启动速度提高 30% — 零代码
通过调整三个JVM和Spring Boot配置开关,无需重写代码即可显著优化Java应用性能:内存减少60%,启动速度提升30%。适用于所有在JVM上运行API的生产团队,低成本实现高效能。
439 3
|
5月前
|
存储 缓存 Java
Java数组全解析:一维、多维与内存模型
本文深入解析Java数组的内存布局与操作技巧,涵盖一维及多维数组的声明、初始化、内存模型,以及数组常见陷阱和性能优化。通过图文结合的方式帮助开发者彻底理解数组本质,并提供Arrays工具类的实用方法与面试高频问题解析,助你掌握数组核心知识,避免常见错误。
|
3月前
|
Java 大数据 Go
从混沌到秩序:Java共享内存模型如何通过显式约束驯服并发?
并发编程旨在混乱中建立秩序。本文对比Java共享内存模型与Golang消息传递模型,剖析显式同步与隐式因果的哲学差异,揭示happens-before等机制如何保障内存可见性与数据一致性,展现两大范式的深层分野。(238字)
108 4
|
3月前
|
存储 缓存 Java
【深入浅出】揭秘Java内存模型(JMM):并发编程的基石
本文深入解析Java内存模型(JMM),揭示synchronized与volatile的底层原理,剖析主内存与工作内存、可见性、有序性等核心概念,助你理解并发编程三大难题及Happens-Before、内存屏障等解决方案,掌握多线程编程基石。
|
4月前
|
缓存 监控 Kubernetes
Java虚拟机内存溢出(Java Heap Space)问题处理方案
综上所述, 解决Java Heap Space溢出需从多角度综合施策; 包括但不限于配置调整、代码审查与优化以及系统设计层面改进; 同样也不能忽视运行期监控与预警设置之重要性; 及早发现潜在风险点并采取相应补救手段至关重要.
640 17
|
8月前
|
存储 缓存 Java
【高薪程序员必看】万字长文拆解Java并发编程!(5):深入理解JMM:Java内存模型的三大特性与volatile底层原理
JMM,Java Memory Model,Java内存模型,定义了主内存,工作内存,确保Java在不同平台上的正确运行主内存Main Memory:所有线程共享的内存区域,所有的变量都存储在主存中工作内存Working Memory:每个线程拥有自己的工作内存,用于保存变量的副本.线程执行过程中先将主内存中的变量读到工作内存中,对变量进行操作之后再将变量写入主内存,jvm概念说明主内存所有线程共享的内存区域,存储原始变量(堆内存中的对象实例和静态变量)工作内存。
259 0
|
5月前
|
监控 Kubernetes Java
最新技术栈驱动的 Java 绿色计算与性能优化实操指南涵盖内存优化与能效提升实战技巧
本文介绍了基于Java 24+技术栈的绿色计算与性能优化实操指南。主要内容包括:1)JVM调优,如分代ZGC配置和结构化并发优化;2)代码级优化,包括向量API加速数据处理和零拷贝I/O;3)容器化环境优化,如K8s资源匹配和节能模式配置;4)监控分析工具使用。通过实践表明,这些优化能显著提升性能(响应时间降低40-60%)同时降低资源消耗(内存减少30-50%,CPU降低20-40%)和能耗(服务器功耗减少15-35%)。建议采用渐进式优化策略。
251 1
|
6月前
|
SQL 缓存 安全
深度理解 Java 内存模型:从并发基石到实践应用
本文深入解析 Java 内存模型(JMM),涵盖其在并发编程中的核心作用与实践应用。内容包括 JMM 解决的可见性、原子性和有序性问题,线程与内存的交互机制,volatile、synchronized 和 happens-before 等关键机制的使用,以及在单例模式、线程通信等场景中的实战案例。同时,还介绍了常见并发 Bug 的排查与解决方案,帮助开发者写出高效、线程安全的 Java 程序。
337 0
|
5月前
|
存储 监控 算法
Java垃圾回收机制(GC)与内存模型
本文主要讲述JVM的内存模型和基本调优机制。