(转载)java内存模型

java并发采用的是共享内存模型,线程之间的通信对程序员来说是透明的,内存可见性问题很容易困扰着java程序员,今天我们就来揭开java内存模型的神秘面纱。



在揭开面纱之前,我们需要认识几个基础概念:内存屏障(memory Barriers),指令重排序,happens-before规则,as-if-serial语义。

什么是 Memory Barrier(内存屏障)?

内存屏障,又称内存栅栏,是一个CPU指令,基本上它是一条这样的指令:
1、保证特定操作的执行顺序。
2、影响某些数据(或则是某条指令的执行结果)的内存可见性。

编译器和CPU能够重排序指令,保证最终相同的结果,尝试优化性能。插入一条Memory Barrier会告诉编译器和CPU:不管什么指令都不能和这条Memory Barrier指令重排序。

Memory Barrier所做的另外一件事是强制刷出各种CPU cache,如一个 Write-Barrier(写入屏障)将刷出所有在 Barrier 之前写入 cache 的数据,因此,任何CPU上的线程都能读取到这些数据的最新版本。

Memory Barrier.png

这和java有什么关系?volatile是基于Memory Barrier实现的。

如果一个变量是volatile修饰的,JMM会在写入这个字段之后插进一个Write-Barrier指令,并在读这个字段之前插入一个Read-Barrier指令。

volatile.png

这意味着,如果写入一个volatile变量a,可以保证:
1、一个线程写入变量a后,任何线程访问该变量都会拿到最新值。
2、在写入变量a之前的写入操作,其更新的数据对于其他线程也是可见的。因为Memory Barrier会刷出cache中的所有先前的写入。

happens-before

从jdk5开始,java使用新的JSR-133内存模型,基于happens-before的概念来阐述操作之间的内存可见性。

在JMM中,如果一个操作的执行结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系,这个的两个操作既可以在同一个线程,也可以在不同的两个线程中。

与程序员密切相关的happens-before规则如下:
1、程序顺序规则:一个线程中的每个操作,happens-before于该线程中任意的后续操作。
2、监视器锁规则:对一个锁的解锁操作,happens-before于随后对这个锁的加锁操作。
3、volatile域规则:对一个volatile域的写操作,happens-before于任意线程后续对这个volatile域的读。
4、传递性规则:如果 A happens-before B,且 B happens-before C,那么A happens-before C。

注意:两个操作之间具有happens-before关系,并不意味前一个操作必须要在后一个操作之前执行!仅仅要求前一个操作的执行结果,对于后一个操作是可见的,且前一个操作按顺序排在后一个操作之前。

指令重排序

在执行程序时,为了提高性能,编译器和处理器会对指令做重排序。但是,JMM确保在不同的编译器和不同的处理器平台之上,通过插入特定类型的Memory Barrier来禁止特定类型的编译器重排序和处理器重排序,为上层提供一致的内存可见性保证。

1、编译器优化重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
2、指令级并行的重排序:如果不存l在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
3、内存系统的重排序:处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

数据依赖性

如果两个操作访问同一个变量,其中一个为写操作,此时这两个操作之间存在数据依赖性。
编译器和处理器不会改变存在数据依赖性关系的两个操作的执行顺序,即不会重排序。

as-if-serial

不管怎么重排序,单线程下的执行结果不能被改变,编译器、runtime和处理器都必须遵守as-if-serial语义。

抽象结构

java线程之间的通信由java内存模型(JMM)控制,JMM决定一个线程对共享变量(实例域、静态域和数组)的写入何时对其它线程可见。

从抽象的角度来看,JMM定义了线程和主内存Main Memory(堆内存)之间的抽象关系:线程之间的共享变量存储在主内存中,每个线程都有自己的本地内存Local Memory(只是一个抽象概念,物理上不存在),存储了该线程的共享变量副本。

所以,线程A和线程B之前需要通信的话,必须经过一下两个步骤:
1、线程A把本地内存中更新过的共享变量刷新到主内存中。
2、线程B到主内存中读取线程A之前更新过的共享变量。

转载自:http://www.jianshu.com/p/d3fda02d4cae
时间: 2024-10-10 09:44:52

(转载)java内存模型的相关文章

Java 内存模型及GC原理 (转载)

一个优秀Java程序员,必须了解Java内存模型.GC工作原理,以及如何优化GC的性能.与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能提高整个应用程序的性能. 本文将从JVM内存模型.GC工作原理,以及GC的几个关键问题进行探讨,从GC角度提高Java程序的性能. 一.Java内存模型 按照官方的说法:Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配. JVM主要管理两种类型内存:堆和非堆,堆内存

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

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

Java内存模型(转载)

1. 概述 多任务和高并发是衡量一台计算机处理器的能力重要指标之一.一般衡量一个服务器性能的高低好坏,使用每秒事务处理数(Transactions Per Second,TPS)这个指标比较能说明问题,它代表着一秒内服务器平均能响应的请求数,而TPS值与程序的并发能力有着非常密切的关系.在讨论Java内存模型和线程之前,先简单介绍一下硬件的效率与一致性. 2.硬件的效率与一致性 由于计算机的存储设备与处理器的运算能力之间有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理

【Todo】【转载】深入理解Java内存模型

参考Infoq上的这篇文章:Link <深入理解Java内存模型(一)--基础>

深入理解Java内存模型--转载

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

JDK 源码阅读 —— Java 内存模型

零. 为什么需要 Java 内存模型 为了让程序员忽略掉各种硬件和操作系统的内存访问差异, 也既无需关心不同架构上内存模型的差异, Java 在代码和硬件内存模型间又提供了一个 Java 内存模型. 一. 并发模型的分类 在并发编程中,需要处理两个关键问题:线程之间如何通信(线程之间以何种机制来交换信息, 有两种方式:共享内存和消息传递)及线程之间如何同步. 在共享内存的并发模型里(如 Java),线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态(主存)来隐式(对程序员透明)进行通

Java内存模型(二)

volatile型变量的特殊规则 volatile是Java虚拟机提供的最轻量级的同步机制,当一个变量被定义成volatile后,它将具备两种特性,第一是保证此变量对所有线程的可见性,这里的“可见性”是指当一条线程改变了这个变量的值后,新值对于其他线程来说是可以立即得知的:第二个语义是禁止指令重排序,普通的变量仅仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获取正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致. 开发人员经常误解的一个描述:“volatile变量对于所有

转: 【Java并发编程】之十七:深入Java内存模型—内存操作规则总结

转载请注明出处:http://blog.csdn.net/ns_code/article/details/17377197 主内存与工作内存 Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节.此处的变量主要是指共享变量,存在竞争问题的变量.Java内存模型规定所有的变量都存储在主内存中,而每条线程还有自己的工作内存,线程的工作内存中保存了该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取.赋值等)都必须在工作内存中

Java内存模型(转)

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