Java内存模型JMM

Java内存模型定义

Java作为一个跨平台的语言,它的实现要面对不同的底层硬件系统,设计一个中间层模型来屏蔽底层的硬件差异,给上层的开发者一个一致的使用接口。Java内存模型就是这样一个中间层的模型,它为程序员屏蔽了底层的硬件实现细节,支持大部分的主流硬件平台。

Java内存模型(Java Memory Mode):Java内存模型是java虚拟机内存如何与计算机内存(RAM)一起工作。

Java虚拟机是整个计算机的模型,所以这个模型自然包含一个内存模型。也可以说JMM是java虚拟机内存使用规范。

通俗来讲,就是描述Java中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和内存中读取变量这样的底层细节。

Java内存模型规定了不同线程如何以及何时可以看到其他线程写入共享变量的值以及如何在必要时同步对共享变量的访问。

注意:Java Memory Model并不是真实存在的,他只是物理内存模型的一个映射。

Java内存模型介绍

JVM中内存分配的两个概念:

1、stack(栈)

        特点:对象生命周期确定、数据大小确定。

        存储数据:基本数据变量、对象引用(句柄)

        位置:缓存、寄存器、写缓冲区。

2、heap(堆)

        特点:运行时动态分配大小、对象声明周期不确定、垃圾回收。

        存储数据:对象

        位置:主内存、缓存

理论上说所有的stack和heap都存储在物理主内存中,但随着CPU运算其数据的副本可能被缓存或者寄存器持有,持有的数据遵从一致性协议。

存储方式

        上面说到,一个对象存储在heap上的,对象中所属的方法与方法的成员变量存储在stack上。一个对象的成员变量随着对象本身存储在堆上,无论该对象类型是引用类型或者是基本类型。静态变量和对象类定义存储在堆上。

并发原因

        存储在堆上的对象可以被持有该对象引用的栈访问。能访问对象,也就是能访问对象中的成员。当两个线程同时访问一个对象时,每个线程都拥有该对象成员变量的私有拷贝。

Java内存模型与系统内存模型

在系统内存架构中并没有栈、堆这种概念,只有寄存器、缓存、主内存。理论上说偶有的栈和堆都存储在主内存中,但随着CPU运算其数据的副本可能被缓存或者寄存器持有。持有的数据遵从CPU-Cache一致性协议。

Java内存模型抽象结构图

主内存:保存了所有的变量。

共享变量:如果一个变量被多个线程使用,那么这个变量会在每个线程的工作内存中保有一个副本,这种变量就是共享变量。

工作内存:每个线程都有自己的工作内存,线程独有,保存了线程用到了变量的副本。工作内存负责与线程交互,也负责与主内存交互。为了更高的效率java虚拟机、硬件系统可能让工作内存有限分配在寄存器、缓存中。

JMM对共享内存的操作做出了如下两条规定:

1、线程对共享内存的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写。

2、不同线程无法直接访问其他线程工作内存中的变量,因此共享变量的值传递需要通过主内存完成。

java并发问题的根源

假设线程A和线程B同时访问某个对象的成员变量x。当线程a需要操作变量a,会将a副本复制到线程A的工作内存中。

当线程a未执行完毕,线程b也要访问变量a。

但是线程a与线程b操作的是自己工作空间中的变量副本。线程a中的副本和线程b中间的副本相互不可见。如果a线程率先完成了任务并写回主存。那么线程b的运算就是在使用后脏数据运算。如果b也写回主存那么线程a的任务就会丢失。

为了保证程序的准确性,我们就需要在并发时添加额外的同步操作。

Java内存模型-内存间的八种同步操作

Java内存模型定义了以下8中操作来完成,它们都是原子操作(除了对long和double类型的变量)。

锁定(lock):作用于主内存中的变量,将他标记为一个线程独享变量。(通常意义上的上锁,就是一个线程正在使用时,其他线程必须等待该线程任务完成才能继续执行自己的任务)

解锁(unlock):作用于主内存中的变量,解除变量的锁定状态,解除锁定状态的变量才能被其他线程锁定。(执行完成后解开锁)

读取(read):作用于主内存中的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。(从主内存 读取到工作内存中)

载入(load):把read操作从主内存中得到的变量值放入工作内存的变量副本中。(给工作内存中的副本赋值)

使用(use):把工作内存中的一个变量的值传给执行引擎,每当虚拟机遇到一个使用到变量的指令时都会使用该指令。(程序执行过程中读取该值时调用)

赋值(assign):作用于工作内存的变量,它把一个从执行引擎接收到的赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。(将运算完成后的新值赋回给工作内存中的变量,相当于修改工作内存中的变量)

存储(store):用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,一边随后write操作使用。(将该值从变量中取出,写入工作内存中)

写入(write):作用于主内存的变量,它将store操作从工作内存中得到的变量的值放入主内存的变量中。(将工作内存中的值写回主内存)

读取执行步骤

写入执行步骤

操作规则

1、不允许read和load、store和write操作单独出现,即不允许一个变量从主存读取了但工作内存不接受,或者从工作内存发起回写了但主内存不接受的情况出现。

2、不允许一个线程丢弃它的最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回到主内存。

3、不允许一个线程无原因地(没有发生过任何assign)把数据从线程的工作内存同步回到主内存中。

4、一个新的变量只能在主内存中“诞生”,不允许在工作内存中直接使用了一个未被初始化(load或assign)的变量,换句话说就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。

5、一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。

6、如果对一个变量执行lock操作,将会清空工作内存中次变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。

7、如果一个变量事先没有被lock操作规定,则不允许对它执行unlock操作,也不允许去unlock一个背其他线程锁定住的变量。

回顾

1、Java内存模型是一个规范,他规定了不同线程如何以及何时可以看到其他线程写入共享变量的值以及如何在必要时同步对共享变量的访问。

2、java内存模型要求,调用栈和本地变量存储在线程栈上,对象存放在堆上。线程之前的通信必须要经过主内存。

3、定义了同步的把个操作,以及使用这把个操作需要遵守的规则。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值