【并发编程】JMM:java内存模型抽象

本文试图向大家解释清楚JMM及其抽象模型,但不仅仅是一个介绍,更希望能讲清楚JMM内存模型抽象的原因。

一、JMM的概念;

二、JMM的抽象将内存内存模型分成线程私有的本地内存和所有线程共享的主存;

三、JMM抽象模型造成了并发编程中共享变量的内存可见性问题,为什么会造成?选择这样的抽象模型有什么好处?有什么样的方法来处理这个问题?

一、JMM

JMM直译过来就是java内存模型(java memory model),他的更深层次的描述,“JMM是一个语言级的内存模型,通过屏蔽各个系统平台的差异,对程序猿提供一个各个系统平台一致的内存模型,他描绘了共享变量在内存中存取抽象过程规定了一个线程操作共享变量的结果何时对其他线程可见。”

二、JMM抽象

java的并发模型是基于内存共享的,通过共享内存之间的公共状态来进行线程通信,这种共享实际上就是共享变量的读写操作

除了上述java的并发模型,还有两条线索引出java内存模型的抽象。第一条,“速度线索”,按照速度从快到慢,“CPU> 寄存器> 高速缓存> 内存> 磁盘缓存> 磁盘”,高速缓存是为了缓解寄存器和内存之间的速度差异,同样的磁盘缓存缓解了内存和磁盘之间的速度差异;第二条,“总线事务线索”,在多CPU的系统中,无论何时,都只有一个CPU能操作内存,这一系列CPU和内存之间的竞争和通信通过总线事务(包含读写事物)来描述,如下图。

基于以上两条线索(细节在下一节描述),java内存模型的抽象可以这样来描述,“每个运行的线程都有一个私有的本地内存,所有线程共享主存,所有线程对共享变量的读操作都首先从本地内存获取,如果没有则从主存获取;所有线程的写操作都首先写到本地内存,在恰当的时机写回主存”。JMM的抽象模型看起来如下图所述。

以上内容通过“基于内存并发模型、速度线索和总线事务线索”引出JMM的抽象模型。还有一些没有讲清楚的地方,下面需要更详细的解释。

三、更多细节

为什么JMM的抽象结果要划分成线程私有的本地内存和所有线程共享的主存

1. 系统模型和JMM的对应

在谈论更多细节之前,先强行把速度线索中描绘的硬件和JMM抽象模型对应,实际上JMM的抽象也基于此,要结合计算机组成原理来理解这部分内容。

2. 本地内存引发的并发问题

首先明确的前提是java的并发模型是基于内存读写共享变量来进行通信的,线程之间要进行通信,只有通过读写主存,也就是JMM通过控制每个线程的本地内存与主存之间的交互,来向程序猿提供内存可见性保证。基于这样的描述,其实JMM是可以跳开本地内存的,直接操作主存进行线程通信会更简单更可靠。引入本地内存还会导致一些不好的结果,如下图所述,A/B两个线程并行操作一个拥有共享变量(实际上就是对外可以访问的实例变量)的对象,该对象初始化完成的时候a、b变量都进行了默认初始化,即a=b=0,存在堆内存中(主存),操作开始之前,AB两个线程都会获得ab变量的副本存在于本地内存当中,当执行到a=1的时候,将1赋值给本地内存中的a,注意是本地内存,而不是主存中的a,同样的b=2,也只是将2赋值给B线程中的b变量而并没有写到主存,此时对于xy的赋值操作,都会从主存中获取,从而出现一个可能的执行结果,a=1,b=2,x=0,y=0。

然而直接操作主存,就不会出现这样的结果,因为任意时刻都只会有一颗CPU获得总线事务,也就意味着任意时刻,都只有一个线程可以操作共享变量,但是基于此模型的代码执行速度谁能忍受?为了表明这一原因,“速度线索”中高速缓存缓解内存和寄存器之间的速度不匹配问题,“缓解”的过程是将刷新到主存的内容保存到高速缓存中,积累到一定量再一次性刷到内存(此时其他线程才可见),这样的好处可以通过java传统IO流中缓冲流提高速度的原理想象得到,正如“总线事务线索”中描述那样,集中刷主存,减少了总线事务竞争的次数和总线占用,从而获得更高的性能。

3. 解决问题

如何解决?这正是多线程中同步的意义。同步做了那些事情?同步保证了原子性、互斥性、内存可见性和顺序系(禁止重排序)。同步的内容谈起来也有很多内容,不只是一个简简单单的使用synchronized或Lock,而更多的是希望能讲明白synchronized和lock表达的内存语义,这一部分会抽时间单独来写。

四、补充

本文只涉及java并发编程最开始的一些浅显内容,其他更多的内容并未涉及,后续会抽时间逐步完善。

附注:

本文如有错漏,烦请不吝指正,谢谢!

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-11 10:22:48

【并发编程】JMM:java内存模型抽象的相关文章

3.java并发编程艺术-java内存模型

3.1 java内存模型的基础 3.1.1并发编程模型的两个关键问题 在并发编程中,需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指两个线程 之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. 在共享内存的并发模型里,线程之间共享程序的公共状态,通过写-读内存中的公共状态进行隐式通信.在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过发送消息来进行显式进行通信. 同步是指程序中用于控制不同线程

java并发编程12.java内存模型

假设一个线程为变量赋值:variable = 3: 内存模型需要解决一个问题:“在什么条件下,读取variable的线程将看到这个值为3?” 这看上去理所当然,但是如果缺少同步,那么将会有许多因素使得线程无法立即甚至永远,看到另一个线程的操作结果. 如: 1.在编译器中生成的指令顺序,可以与源代码中的顺序不同,此外编译器还会将变量保存在寄存器而不是内存中: 2.处理器可以采用乱序或并行等方式来执行指令: 3.缓存可能会改变将写入变量提交到主内存的次序: 4.而且保存在处理器本地缓存中的值,对于其

并发编程之java内存模型(Java Memory Model ,JMM)

一.图例 0.两个概念 Heap(堆):运行时的数据区,由垃圾回收负责,运行时分配内存(所以慢),对象存放在堆上 如果两个线程,同时调用同一个变量,怎两个线程都拥有,该对象的私有拷贝 (可以看一下,ThreadLocal:   引用注明出处,https://www.cnblogs.com/xiaonantianmen/p/9151481.html) Stack(栈):存放一些引用变量 二.多cpu情况 0.JVM与物理内存之间的通信. 2.线程之间的通信必须通过主内存(此处则是要考虑synchr

【Java并发基础】Java内存模型解决有序性和可见性

前言 解决并发编程中的可见性和有序性问题最直接的方法就是禁用CPU缓存和编译器的优化.但是,禁用这两者又会影响程序性能.于是我们要做的是按需禁用CPU缓存和编译器的优化. 如何按需禁用CPU缓存和编译器的优化就需要提到Java内存模型.Java内存模型是一个复杂的规范.其中最为重要的便是Happens-Before规则.下面我们先介绍如何利用Happens-Before规则解决可见性和有序性问题,然后我们再扩展简单介绍下Java内存模型以及我们前篇文章提到的重排序概念. volatile 在前一

JMM java内存模型

JMM对于一个想要深入了解java的程序猿来说是不可避免的一关,本文偏理论性,尽可能说的通俗易懂,如有不对的地方希望多多指正. 那我们先说一下jvm的主内存分配 1 java虚拟机栈(java virtual stack) 虚拟机栈是线程私有的,每个线程都有一个自己的虚拟机栈,是java方法执行的内存模型,每个方法执行的时候都会在虚拟机栈上创建一个栈帧,栈帧是一个数据结构,主要存储的是方法中的局部变量(基本类型,对象的引用,returnAddress类型(指向一条字节码指令的地址)),操作栈(指

Java并发编程--7.Java内存操作总结

主内存和工作内存 工作规则 Java内存模型, 定义变量的访问规则, 即将共享变量存储到内存和取出内存的底层细节  所有的变量都存储在主内存中,每条线程有自己的工作内存,工作内存中用到的变量, 是从主内存拷贝的副本,线程对变量的所有操作都在工作内存中进行, 线程间变量值得传递均需通过主内存来完成 内存间交互操作 1.luck(锁定):作用于主内存的变量,它把一个变量标示为一条线程独占的状态. 2.unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其

多线程并发之java内存模型JMM

多线程概念的引入是人类又一次有效压寨计算机的体现,而且这也是非常有必要的,因为一般运算过程中涉及到数据的读取,例如从磁盘.其他系统.数据库等,CPU的运算速度与数据读取速度有一个严重的不平衡,期间如果按一条线程执行将会在很多节点产生阻塞,使计算效率低下.另外,服务器端是java最擅长的领域,作为服务器必须要能同时响应多个客户端的请求,同样需要多线程的支持.在多线程情况下,高并发将带来数据的共享与竞争问题,tomcat作为中间件将多线程并发等细节尽量封装起来处理,使用户对多线程透明,更多地关注业务

聊聊Java内存模型

一.Java内存模型 硬件处理 电脑硬件,我们知道有用于计算的cpu.辅助运算的内存.以及硬盘还有进行数据传输的数据总线.在程序执行中很多都是内存计算,cpu为了更快的进行计算会有高速缓存,最后同步至主内存,大概的交互如下图 为了使处理器内部的运算单元能够被充分的利用,处理器可能会对输入代码进行乱序执行优化,然后将计算后的结果进行重组,保证该结果和顺序执行的结果是一致的(单位时间内,一个core只能执行一个线程,所以结果的一致仅限一个线程内). Java内存模型 Java内存模型是语言级别的模型

Java 内存模型与线程

when ? why ? how ? what ? 计算机的运行速度和它的存储和通信子系统速度的差距太大,大量的时间都花费在磁盘I/O .网络通信或者数据库访问上.如何把处理器的运算能力"压榨"出来? 如何充分利用计算机处理器? 因为绝大多数的运算任务都不可能只靠处理器"计算"就能完成,处理器至少要与内存交互,如读取运算数据.存储运算结果这个 I/O 操作是很难消除的.又因为存储设备和处理器运算速度有几个数量级差距,所以在内存和处理器之间加了个高速缓存(这样处理器就