JMM与happens-before
happens-before是JMM最核心的概念,理解happens-before是理解JMM的关键。
一.JMM的设计
首先,让我们先分析一下JMM的设计意图。从JMM的设计者的角度,在设计JMM的时候要考虑一下两个关键因素:
1.程序员对内存模型的使用。程序员希望内存模型易于理解、易于编程。程序员希望基于一个强内存模型来编写代码。
2.编译器和处理器对内存模型的实现。编译器和处理器希望内存模型对它们的束缚越少越好,这样它们就可以做尽可能多的优化来提高性能。编译器和处理器希望实现一个弱内存模型。
上述两个因素相互矛盾,所以JSR-133专家组在设计JMM时的核心目标就是找到一个好的平衡点:一方面,要为程序员提供足够强的内存可见性保证;另一方面,对编译器和处理器的限制要尽可能地放松。下面让我们来看JSR-133是如何实现这一目标的。
double pi = 3.14; // A
double r = 1.0; // B
double area = pi * r * r; // C
上述计算圆的面积的代码中,存在三个happens-before关系:
A happens-before B
B happens-before C
A happens-before C
在者三个happens-before关系中2和3是必须的,1是不必要的。因此JMM把happens-before要求禁止的重排序分了下面两类
1.会改变程序执行结果的重排序
2.不会改变程序执行结果的重排序
JMM对这两种不同性质的重排序,采用了不同的策略,如下:
1.对于会改变程序执行结果的重排序,JMM要求编译器和处理器必须禁止这种重排序
2.对于不会改变程序执行结果的重排序,JMM对编译器和处理器不做要求(JMM允许这种重排序)
下图是JMM的设计示意图

JMM向程序员提供的happens-before规则能满足程序员的要求,JMM的happens-before规则不但简单易懂,而且也向程序员提供了足够强的内存可见性保证。
JMM对编译器和处理器的束缚已经尽可能少。从上面的分析可以看出,JMM其实是在遵循一个基本原则:只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都行。例如,如果编译器经过细致的分析后,认定一个锁只会被单个线程访问,那么这个锁可以被消除。再如,如果编译器经过细致的分析后,认定一个volatile变量只会被单个线程访问,那么编译器可以把这个volatile变量当作一个普通变量来对待。这些优化既不会改变程序的执行结果,又能提高程序的执行效率。
二.happens-before的定义
happens-before的概念最初由Leslie Lamport在其一篇影响深远的论文(《Time,Clocks andthe Ordering of Events in a Distributed System》)中提出。Leslie Lamport使用happens-before来定义分布式系统中事件之间的偏序关系(partial ordering)。Leslie Lamport在这篇论文中给出了一个分布式算法,该算法可以将该偏序关系扩展为某种全序关系。
JSR-133使用happens-before的概念来指定两个操作之间的执行顺序。由于这两个操作可以在一个线程之内,也可以是在不同线程之间。因此,JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证(如果A线程的写操作a与B线程的读操作b之间存在happensbefore关系,尽管a操作和b操作在不同的线程中执行,但JMM向程序员保证a操作将对b操作可见)。
《JSR-133:Java Memory Model and Thread Specification》对happens-before关系的定义如下:
1.如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
2.两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执行。如果重排序之后的执行结果,与按happens-before关系来执行的结果一致,那么这种重排序并不非法。
上面1是JMM对程序员的承诺。从程序员的角度来说,可以这样理解happens-before关系:如果A happens-before B,那么Java内存模型将向程序员保证——A操作的结果将对B可见,且A的执行顺序排在B之前。注意,这只是Java内存模型向程序员做出的保证!
上面2是JMM对编译器和处理器冲排序的约束。MM其实是在遵循一个基本原则:只要不改变程序的执行结果,编译器和处理器怎么优化都行。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
5.start规则:如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作
6.join规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
四.锁的内存语义
释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中;获取锁时会把该线程对应的本地内存置为无效。
JMM与happens-before的更多相关文章
- 浅析java内存模型--JMM(Java Memory Model)
在并发编程中,多个线程之间采取什么机制进行通信(信息交换),什么机制进行数据的同步? 在Java语言中,采用的是共享内存模型来实现多线程之间的信息交换和数据同步的. 线程之间通过共享程序公共的状态,通 ...
- JMM(java内存模型)
What is a memory model, anyway? In multiprocessorsystems, processors generally have one or more laye ...
- JMM和happens-before原则
JMM: Java Memory Model(Java内存模型),围绕着在并发过程中如何处理可见性.原子性.有序性这三个特性而建立的模型. 可见性: JMM提供了volatile变量定义.final. ...
- 对Java内存模型即JMM的理解
类似物理上的计算机系统,Java虚拟机规范中也定义了一种Java内存模型,即Java Memory Model(JMM),来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能 ...
- Java 线程 — JMM Java内存模型
JMM Java Memory Model,Java内存模型,属于语言级的内存模型 并发编程中存在的问题: 如何通信:用于线程之间交换信息.两种方式:共享内存,消息传递 如何同步:用于控制不同线程间操 ...
- Java内存模型--JMM简介
JMM:Java Memory Model(Java内存模型),围绕着在并发过程中如何处理可见性.原子性.有序性这三个特性而建立的模型. 可见性:JMM提供了volatile变量定义,final.sy ...
- JMM & synchronized概述(转)
根据Java语言规范中的说明,JVM系统中存在一个主内存(Main Memory),Java中所有的变量存储在主内存中,对于所有的线程是共享的(相当于黑板,其他人都可以看到的).每个线程都有自己的工作 ...
- Java多线程 -- 深入理解JMM(Java内存模型) --(五)锁
锁的释放-获取建立的happens before 关系 锁是Java并发编程中最重要的同步机制.锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息. 下面是锁释放-获取的示例代 ...
- Java内存模型JMM与可见性
Java内存模型JMM与可见性 标签(空格分隔): java 1 何为JMM JMM:通俗地讲,就是描述Java中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这 ...
- 从一个简单的Java单例示例谈谈并发 JMM JUC
原文: http://www.open-open.com/lib/view/open1462871898428.html 一个简单的单例示例 单例模式可能是大家经常接触和使用的一个设计模式,你可能会这 ...
随机推荐
- SimdJsonSharp:每秒解析千兆字节的JSON
SimdJsonSharp: Parsing gigabytes of JSON per second C# version of lemire/simdjson (by Daniel Lemire ...
- Dapper - 一款轻量级对象关系映射(ORM)组件,DotNet 下
Dapper - a simple object mapper for .Net Official Github clone: https://github.com/SamSaffron/dapper ...
- sso单点登录的入门(Session跨域、Spring-Session共享)
1.单点登录,就是多系统,单一位置登录,实现多系统同时登录的一种技术.单点登录一般是用于互相授信的系统,实现单一位置登录,全系统有效的. 区分与三方登录(第三方登录) ,三方登录:某系统,使用其他系统 ...
- SQLMap常用教程
先安装 python环境(2.6.x或2.7版本) ,再将SQLMap 放在安装目录下 注意:sqlmap只是用来检测和利用sql注入点的,并不能扫描出网站有哪些漏洞,使用前请先使用扫描工具扫出sql ...
- Java面向对象——类的成员
Java面向对象——类的成员 摘要:本文主要介绍了类的常见成员. 属性 属性称为成员变量,一般来讲不用赋值,因为有默认值,另外显式赋值没有意义会导致所有由此类创建对象都是此值. 默认值 Boolean ...
- 浅谈cookie 和 session
一. cookie 定义:保存在浏览器本地上的一组组键值对 特点: 由服务器让浏览器进行设置的 浏览器保存在浏览器本地 下次访问时自动携带 应用: 登录 保存浏览习惯 简单的投票 使用cookie的原 ...
- pm2入门
简介 PM2是node进程管理工具,可以利用它来简化很多node应用管理的繁琐任务,如性能监控.自动重启.负载均衡等,而且使用非常简单. 安装 全局安装,简直不能更简单. npm install -g ...
- Windows下面startup.bat启动Tomcat偶发死锁问题
Windows下面startup.bat启动Tomcat时,因为日志都打印到了cmd里面,存在偶发卡死Tomcat的问题,该问题确认是Windows系统的问题,而且一直没有解决.解决的办法是把日志重定 ...
- Windows隐藏用户小技巧
0x01 前言 本文目的在于对于Windows账户访问与控制的技术理论进行分析与总结,熟悉了解Windows账户的特性及相关操作,并依此进行Windows账户的相关实验,进行实验记录与总结.通过本文记 ...
- c# 第8节 变量、变量名命令规则、作用域、@的作用
本节内容: 1:变量 2:变量名命令规则 3:常量 4:变量和常量的作用域 5:@的作用 1:变量是什么? 计算即中存储变量的也是三步骤: 实例: 2:变量命令规则 练习: 3.常量 实现: 4:变量 ...