1.Java内存模型概述

  Java内存模型是一种抽象概念,不是真实存在的。主要定义了程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存取出变量这样的底层细节。注意:这里的变量仅包括实例字段、静态字段、构成数组对象的元素,但不包括局部变量与方法参数。因为后者是线程私有的,不会被共享,自然就不存在竞争问题。

2.主内存与工作内存

  Java内存模型规定了所有的变量都存储在主存中。每条线程还有自己的工作内存,工作内存中保存了该线程使用到的变量的主内存拷贝副本,线程对变量的操作都在工作内存中进行,不能直接读写主存中的变量。不同的线程之间也无法访问对方工作内存中的变量。线程间变量的传递需要通过主内存来完成。

线程、工作内存、主内存,之间的关系如下图:

内存间的交互操作

  即一个变量如何从工作内存同步到主内存,如何从主内存拷贝到工作内存之间的实现细节。

  Java内存模型定义了8中交互动作,虚拟机实现时必须保证每一种操作都是原子的、不可再分的。

  • lock(锁定):作用于主内存中的变量,他把一个变量标识为一个线程独占的状态。
  • unlock(解锁):作用于主内存中的变量,把一个变量从锁定状态释放出来,释放后才能被其他线程线程锁定。
  • read(读取):作用于主内存中的变量,把主存中的变量的值(注意与变量区分开)传输到线程的工作内存中。以便随后的load动作使用。
  • load(载入):作用于工作内存中的变量,把read操作从主存中读取到的变量的值放入工作内存中的变量副本中。
  • use(使用):作用于工作内存中的变量,把工作内存中的变量的值传递给执行引擎。每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于工作内存中的变量,把一个执行引擎接收到的值赋给工作内存中的变量。每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存中的变量,把工作内存中一个变量的值传送到主内存中,以便随后的write操作。
  • write(写入):作用于主内存中的变量,把store操作从工作内存中得到的值放入主内存的变量中。

如果把一个变量从主内存复制到工作内存,那么就要顺序的执行read、load操作。如果要把变量从工作内存同步到主内存中,就要顺序的执行store、write操作。注意:Java内存模型只要求上述两种操作必须按顺序执行,并没有保证是连续执行。也就是说,read和load、store和write之间是可插入其他指令的,例如对主内存中变量a和b进行访问时,可能出现顺序是read a、read b、load b、load  a。初次之外Java内存模型还规定了在执行上述8中操作时必须保证如下规则。

  • 不允许read、load、store、write,操作之一单独出现,即不允许一个变量从主存中读取了但是工作内存中不接受的情况,或者工作内存发起了回写操作但主内存不接受的情况。
  • 不允许一个线程放弃它的最近assign操作,即变量在工作内存中发生了改变必须同步回主内存中。
  • 不允许一个无原因的(即没有发生过任何assign赋值动作)把数据从工作内存同步到主内存。
  • 变量只能在主内存中诞生,不允许工作内存直接使用未初始化的变量。
  • 变量在同一时刻只允许一条线程对于lock操作,但lock操作可被同一线程执行多次,多次执行后只有执行相同次数的unlock,变量才会被解锁。
  • 对变量进行lock操作会清空工作内存中变量的值,使用此变量时会重新从主内存中进行获取。
  • 变量没被lock锁定就不允许对其unlock解锁。也不允许去解锁其他线程锁定住的变量。
  • 对变量进行unlock解锁前,必须把此变量同步到主内中。

对于volatile型变量的特殊操作:可以参考这篇博客<volatile关键字解析>

3.原子性、可见性与有序性

  • 原子性:是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程干扰。
  • 可见性:指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。
  • 有序性:指程序执行的顺序按照代码的先后顺序执行。

4.先行发生原则

  先行发生是指Java内存模型中定义的两项操作之间的偏序关系。如果说操作A先行发生于操作B,也就是说发生操作B之前,操作A产生的影响能被操作B观察到。“影响”包括修改了共享变量的值、发送了消息、调用了方法等。

  下面举个栗子来促进下理解:

//线程A中执行

int i = 1;

//线程B中执行

int j = i;

//线程C中执行

i = 2;

  假设线程A优先于线程B执行,根据先行发生原则,待线程B执行后 j 的值一定等于1,不考虑C的情况。现在假设C介于A、B之间,没有先行发生原则,那么 j 的值我们就无法判断了。因为线程C对i的影响可能会被B观察到也可能不会,这时候线程B就存在读取到过期数据的风险,不具备多线程安全性。

Java模型中一些天然的先行发生关系

  • 程序次序规则:在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于在后面的操作。换句话说就是控制流程顺序
  • 管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作。
  • volatile变量规则:volatile变量的写操作先行发生于后面对这个变量的读操作。
  • 线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作。
  • 线程终止规则:线程的所有操作都先行发生于对此线程的终止检测。我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段来检测线程是否终止。
  • 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。
  • 对象终结规则:一个对象的初始化完成先行发生于它的finalize()方法的开始。
  • 传递性:操作a先行发生于操作b,操作b先行发生于操作c,那么就可以得出操作a先行发生于操作c。

Java内存模型探秘的更多相关文章

  1. JVM学习(3)——总结Java内存模型

    俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 为什么学习Java的内存模式 缓存一致性问题 什么是内存模型 JMM(Java Memory Model)简 ...

  2. 浅析java内存模型--JMM(Java Memory Model)

    在并发编程中,多个线程之间采取什么机制进行通信(信息交换),什么机制进行数据的同步? 在Java语言中,采用的是共享内存模型来实现多线程之间的信息交换和数据同步的. 线程之间通过共享程序公共的状态,通 ...

  3. JMM(java内存模型)

    What is a memory model, anyway? In multiprocessorsystems, processors generally have one or more laye ...

  4. 《深入理解Java内存模型》读书总结

    概要 文章是<深入理解Java内容模型>读书笔记,该书总共包括了3部分的知识. 第1部分,基本概念 包括"并发.同步.主内存.本地内存.重排序.内存屏障.happens befo ...

  5. Java内存模型深度解析:final--转

    原文地址:http://www.codeceo.com/article/java-memory-6.html 与前面介绍的锁和Volatile相比较,对final域的读和写更像是普通的变量访问.对于f ...

  6. Java内存模型深度解析:volatile--转

    原文地址:http://www.codeceo.com/article/java-memory-4.html Volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特 ...

  7. Java内存模型深度解析:顺序一致性--转

    原文地址:http://www.codeceo.com/article/java-memory-3.html 数据竞争与顺序一致性保证 当程序未正确同步时,就会存在数据竞争.java内存模型规范对数据 ...

  8. Java内存模型深度解析:基础部分--转

    原文地址:http://www.codeceo.com/article/java-memory-1.html 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何 ...

  9. 深入理解java内存模型系列文章

    转载关于java内存模型的系列文章,写的非常好. 深入理解java内存模型(一)--基础 深入理解java内存模型(二)--重排序 深入理解java内存模型(三)--顺序一致性 深入理解java内存模 ...

随机推荐

  1. 大疆2019校招FPGA笔试总结

    1.对于同步fifo,每100个cycle可以写入80个数据,每10个cycle可以读出8个数据,fifo的深度至少为? 写时钟频率 w_clk, 读时钟频率 r_clk, 写时钟周期里,每B个时钟周 ...

  2. 20175201课下作业 MyCP

    要求 编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能,要求MyCP支持两个参数: java MyCP -tx XXX1.txt XXX2.bin 用来把文本文件(内容为十进 ...

  3. T-SQL语言基础(1)之理论背景

    从学校就开始接触和使用 SQL 了,但一直没有怎么细细去了解它,最近入职的公司比较重 T-SQL 部分,所以就准备系统的学习一下. 买了一本<Microsoft SQL Server 2008 ...

  4. if(a)

    let a = undefined; let b = null; let c = ''; let d = ; let e = {}; let f = []; if (a) { console.log( ...

  5. DDD领域驱动

    DDD领域驱动领域驱动模型.模型驱动代码接触到需求第一步就是考虑领域模型,而不是将其切割成数据和行为,然后数据用数据库实现,行为使用服务实现,最后造成需求的首肢分离.DDD让你首先考虑的是业务语言而不 ...

  6. VS2017 异常 Editor or Editor Extension

    KE遇到的第一个问题 VS 2017 打开文件的时候, 遇到异常 检查 activity_log发现是 Editor or Editor Extension, 解决办法: 安装插件, Clear ME ...

  7. Python模拟ICMP包

    主要使用Scapy来完成 基础环境 VM1(192.168.1.226) | | VM2(192.168.1.125) vm1封装icmp包发给vm2 vm1脚本: #! /usr/bin/env p ...

  8. 【stm32开发之HAL采坑记之:001使用ST-LINK下载出错问题】

    使用ST-LINK下载出错问题 开发环境:swstm32+stm32cubeMx+硬石F1开发板 联系方式:yexiaopeng1992@126.com 出现问题:在使用ST-linik下载时发现,在 ...

  9. DotNetty网络通信框架学习

    p{ text-align:center; } blockquote > p > span{ text-align:center; font-size: 18px; color: #ff0 ...

  10. 在Ubuntu16.04中python环境下实现tab键补全

    1.编写tab.py的代码: 1 #!/usr/bin/env python 2 # python startup file 3 import sys 4 import readline 5 impo ...