并发研究之Java内存模型(Java Memory Model)
Java内存模型JMM
java内存模型定义
上一遍文章我们讲到了CPU缓存一致性以及内存屏障问题。那么Java作为一个跨平台的语言,它的实现要面对不同的底层硬件系统,设计一个中间层模型来屏蔽底层的硬件差异,给上层的开发者一个一致的使用接口。Java内存模型就是这样一个中间层的模型,它为程序员屏蔽了底层的硬件实现细节,支持大部分的主流硬件平台。
java内存模型(Java Memory Mode):java内存模型是java虚拟机内存如何与计算机内存(RAM)一起工作。java虚拟机是是整个计算机的模型,所以这个模型自然包含一个内存模型。也可以说JMM是java虚拟机内存使用规范。
通俗的来讲,就是描述Java中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节。
Java内存模型规定了不同线程如何以及何时可以看到其他线程写入共享变量的值以及如何在必要时同步对共享变量的访问。
注意:Java Memory Model并不是真实存在的,他只是物理内存模型的一个映射。
Java 内存模型介绍

JVM中内存分配的两个概念:
stack(栈)
特点: 存取速度快、对象生命周期确定、数据大小确定。
存储数据:基本类型变量、对象引用(句柄)
位置:缓存、寄存器、写缓冲区。heap(堆)
特点: 存取速度慢、运行时动态分配大小、对象生命抽周期不确定、垃圾回收。
存储数据:对象
位置:主内存、缓存
理论上说所有的stack和heap都存储在物理主内存中,但随着CPU运算其数据的副本可能被缓存或者寄存器持有,持有的数据遵从一致性协议.
存储方式
上面说到,一个对象时存储在heap上的,对象中所属的方法与方法的成员变量存储在stack上。一个对象的成员变量随着对象本身存储在堆上,无论该对象类型是引用类型或者是基本类型。 静态变量和对象类定义存储于堆上。
并发原因
存储在堆上的对象可以被持有该对象引用的栈访问。能访问对象,也就能访问该对象中的成员变量。当了两个线程同时访问一个对象时,每个线程都拥有该对象成员变量的私有拷贝。
这里只是粗略分配了java内存模型。具体细节的内存分配请查看
JVM内存管理概述
Java内存模型与系统内存模型
我们来看看一个关系图:

在系统内存架构中并没有栈(stack)、堆(heap)这种概念,只有寄存器(register)、缓存(cache)、主内存(RAM、Main Memory)。理论上说所有的栈和堆都存储在主内存中,但随着CPU运算其数据的副本可能被缓存或者寄存器持有。持有的数据遵从CPU-Cache一致性协议。
CPU内存模型、一致性协议可以参考前一篇文章死磕并发之CPU缓存一致性协议(MESI)
Java 内存模型抽象结构图

主内存:保存了所有的变量。
共享变量:如果一个变量被多个线程使用,那么这个变量会在每个线程的工作内存中保有一个副本,这种变量就是共享变量。
比如成员变量、静态变量、数组元素等。
工作内存:每个线程都有自己的工作内存,线程独享,保存了线程用到了变量的副本(主内存共享变量的一份拷贝)。工作内存负责与线程交互,也负责与主内存交互。为了更高的效率java虚拟机、硬件系统可能让工作内优先分配在寄存器、缓存中。
JMM对共享内存的操作做出了如下两条规定:
- 线程对共享内存的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写。
- 不同线程无法直接访问其他线程工作内存中的变量,因此共享变量的值传递需要通过主内存完成。
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操作从工作内存中得到的变量的值放入主内存的变量中。
将工作内存中的值写回主内存。
读取执行步骤

写入执行步骤

操作规则
不允许read和load、store和write操作之一单独出现,即不允许一个变量从主内存读取了但工作内存不接受,或者从工作内存发起回写了但主内存不接受的情况出现。
不允许一个线程丢弃它的最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存。
不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存中。
一个新的变量只能在主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量,换句话说就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。
一个变量在同一个时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。
如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。
如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作,也不允许去unlock一个被其他线程锁定住的变量。
回顾
1.Java内存模型是一个规范,他规定了不同线程如何以及何时可以看到其他线程写入共享变量的值以及如何在必要时同步对共享变量的访问。
2.java内存模型要求,调用栈和本地变量存储在线程栈上,对象存放在堆上。线程之间的通信必须要经过主内存。
3.定义了同步的八个操作,以及使用这八个操作需要遵守的规则。
并发研究之Java内存模型(Java Memory Model)的更多相关文章
- Java内存模型(Java Memory Model,JMM)
今天简单聊聊什么叫做 Java 内存模型,不是 JVM 内存结构哦. JMM 是一个语言级别的内存模型,处理器的硬件模型是硬件级别,Java中的内存模型是内存可见性的基本保证.从而为我们 volati ...
- Java 内存模型- Java Memory Model
多线程越来越多的使用,使得我们需要对它的深入理解.那么就涉及到了Java内存模型JMM.JMM是JVM的一部分,JMM定义了一个线程修改了一个共享变量,其他线程什么时候或者如何看到这个变量,如何去访问 ...
- 【深入理解JAVA虚拟机】第5部分.高效并发.1.Java内存模型与线程。
1.概述 摩尔定律:描述处理器晶体管数量与运行效率之间的发展关系.Amdahl定律:通过系统中并行化与串行化的比重来描述多处理器系统能获得的运算加速能力. 从摩尔定律到Amdahl定律的转变,代表了近 ...
- 并发编程之Java内存模型
在介绍Java内存模型之前,先来了解一下为什么要有内存模型,以及内存模型是什么.然后我们基于对内存模型的了解,学习Java内存模型以及并发编程的三大特性. 为什么要有内存模型 在计算机中,所有的运算操 ...
- java高并发系列 - 第7天:volatile与Java内存模型
public class Demo09 { public static boolean flag = true; public static class T1 extends Thread { pub ...
- Java 内存模型都不会,就敢在简历上写熟悉并发编程吗
从 PC 内存架构到 Java 内存模型 你知道 Java 内存模型 JMM 吗?那你知道它的三大特性吗? Java 是如何解决指令重排问题的? 既然CPU有缓存一致性协议(MESI),为什么 JMM ...
- Java并发编程:JMM (Java内存模型) 以及与volatile关键字详解
目录 计算机系统的一致性 Java内存模型 内存模型的3个重要特征 原子性 可见性 有序性 指令重排序 volatile关键字 保证可见性和防止指令重排 不能保证原子性 计算机系统的一致性 在现代计算 ...
- 高效并发一 Java内存模型与Java线程(绝对干货)
高效并发一 Java内存模型与Java线程 本篇文章,首先了解虚拟机Java 内存模型的结构及操作,然后讲解原子性,可见性,有序性在 Java 内存模型中的体现,最后介绍先行发生原则的规则和使用. 在 ...
- 硬件内存模型到 Java 内存模型,这些硬核知识你知多少?
Java 内存模型跟上一篇 JVM 内存结构很像,我经常会把他们搞混,但其实它们不是一回事,而且相差还很大的,希望你没它们搞混,特别是在面试的时候,搞混了的话就会答非所问,影响你的面试成绩,当然也许你 ...
- Java虚拟机12:Java内存模型
什么是Java内存模型 Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽掉各种硬件和操作系统的访问差异,以实现让Java程序在各种平台下都能达到一致 ...
随机推荐
- 十二、java_网络编程
目录: 一.网络基础 二.TCP/IP协议 三.IP地址 四.Socket通信 一.网络基础 什么是计算机网络: 把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大.功能强的网络系 ...
- ajaxSubmit 提交form 表单
$(document).on('click','#submit_title_btn',function(){ var title = $("#title").val(); if ( ...
- pyglet -- 视频播放器 (简单实现,效果不是太好,切换资源会卡死)(三)
实现一个简单的视频播放器,效果不是很好.这里不多说,直接贴代码了. #-*- coding:gbk -*- import pyglet import os from pyglet.gl import ...
- Nginx负载均衡中后端节点服务器健康检查的一种简单方式
摘自:https://cloud.tencent.com/developer/article/1027287 一.利用nginx自带模块ngx_http_proxy_module和ngx_http_u ...
- 函数和常用模块【day04】:函数参数及调用(二)
本节内容 1.为什么要有参数 2.返回值 3.有参数函数调用 一.为什么要有参数? 无参数实现 def CPU报警邮件() #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接 def 硬盘报警邮件() ...
- 01-VS充当IIS的配置步骤
一. 背景 在实际开发中,经常会遇到需要在线调试,比如:第三方支付的回调.App接口借助PostMan工具测试,需要在代码上直接加断点,来进行调试,VS默认是不支持这种方式,需要手动配置一下,才能达到 ...
- Mongodb 副本集
mongodb主从模式就是一个 单副本的应用:没有很好的扩展性和容错性: 副本集的多个副本保证了容错性:主服务器负责整个副本集的读写,副本集定时同步数据:主节点挂掉:副本集会自动选举一个主的服务器: ...
- 【洛谷P1896【SCOI2005】】互不侵犯King
题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 输入输出格式 输入格式: 只有一行,包 ...
- 卷积神经网络(CNN)学习笔记1:基础入门
卷积神经网络(CNN)学习笔记1:基础入门 Posted on 2016-03-01 | In Machine Learning | 9 Comments | 14935 Vie ...
- 一:对程序员来说CPU是什么?
0.开篇 (1)程序是什么? 指示计算机每一步动作的一组指令 (2)程序是由什么组成的? 指令和数据 (3)什么是机器语言? ...