Java内存模型探秘

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。

原文地址:https://www.cnblogs.com/chenpt/p/9783179.html

时间: 2024-10-09 15:13:53

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

深入理解Java内存模型(四)——volatile

volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步.下面我们通过具体的示例来说明,请看下面的示例代码: class VolatileFeaturesExample { //使用volatile声明64位的long型变量 volatile long vl = 0L; public void set(long l) { vl = l;

深入理解Java内存模型(1 ) -- 基础(转载)

原文地址:http://www.infoq.com/cn/articles/java-memory-model-1 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信.在消息传递的并发模型里,线程之间没有公共状态,线

对java内存模型的认识

浅谈java内存模型        不同的平台,内存模型是不一样的,但是jvm的内存模型规范是统一的.其实java的多线程并发问题最终都会反映在java的内存模型上,所谓线程安全无非是要控制多个线程对某个资源的有序访问或修改.总结java的内存模型,要解决两个主要的问题:可见性和有序性.我们都知道计算机有高速缓存的存在,处理器并不是每次处理数据都是取内存的.JVM定义了自己的内存模型,屏蔽了底层平台内存管理细节,对于java开发人员,要清楚在jvm内存模型的基础上,如果解决多线程的可见性和有序性

(第三章)Java内存模型(中)

一.volatile的内存语义 1.1 volatile的特性 理解volatile特性的一个好办法是把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步.下面通过具体的示例来说明,示例代码如下: class VolatileFeaturesExample { volatile Long vl = 0L; //使用volatile声明64位的Long型变量 public void set(Long l) { vl = l; //单个volatile变量的写 } p

java内存模型-volatile

volatile 的特性 当我们声明共享变量为 volatile 后,对这个变量的读/写将会很特别.理解 volatile 特性的一个好方法是:把对 volatile 变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步.下面我们通过具体的示例来说明,请看下面的示例代码: class VolatileFeaturesExample { //使用volatile声明64位的long型变量 volatile long vl = 0L; public void set(long l) {

java内存模型与线程(转) good

java内存模型与线程 参考 http://baike.baidu.com/view/8657411.htm http://developer.51cto.com/art/201309/410971_all.htm http://www.cnblogs.com/skywang12345/p/3447546.html 计算机的CPU计算能力超强,其计算速度与 内存等存储 和通讯子系统的速度相比快了几个数量级, 数据加载到内存中后,cpu处理器运算处理时,大部分时间花在等待获取去获取磁盘IO.网络通

Java内存模型与垃圾回收

1.Java内存模型 Java虚拟机在执行程序时把它管理的内存分为若干数据区域,这些数据区域分布情况如下图所示: 程序计数器:一块较小内存区域,指向当前所执行的字节码.如果线程正在执行一个Java方法,这个计数器记录正在执行的虚拟机字节码指令的地址,如果执行的是Native方法,这个计算器值为空. Java虚拟机栈:线程私有的,其生命周期和线程一致,每个方法执行时都会创建一个栈帧用于存储局部变量表.操作数栈.动态链接.方法出口等信息. 本地方法栈:与虚拟机栈功能类似,只不过虚拟机栈为虚拟机执行J

深入理解Java内存模型-volatile

volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这些单个读/写操作做了同步.下面我们通过具体的示例来说明,请看下面的示例代码: class VolatileFeaturesExample { volatile long vl = 0L; //使用volatile声明64位的long型变量 public void set(long l) { vl =

深入java内存模型(一)

最近本来想深入学习一下java线程,很想知道其中实现的原理,比如线程资源的共享,线程私有空间,以及线程直接的同步控制等.如果能了解它的实现,对于深入学习线程,会有很大的帮助.最近正在看一份<深入java内存模型>的资料.讲的就是java线程方面的实现原理,拿出来分享一下. 说到线程,我们首先想到的是线程的通信.学习操作系统时,线程通信有两种,一种是通过共享内存,另一种是通过消息传递.共享内存属于隐式的通信,线程之间共享一块内存,线程都可以往这块内存写数据,读数据就实现了通信.而消息传递时一个显