一、happens-before

    happens-before是JMM最核心的概念。对于Java程序员来说,理解happens-before是理解JMM的关键。

  1.1 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

    上面计算圆的面积的示例代码存在3个happens-before关系,如下:

    A happens-before B。

    B happens-before C。

    A happens-before C。

    在3个happens-before关系中,2和3是必需的,但1是不必要的。因此,JMM把happens-before要求禁止的重排序分为了下面两类:

    1、会改变程序执行结果的重排序。

    2、不会改变程序执行结果的重排序。

    JMM对这两种不同性质的重排序,采取了不同的策略,如下:

    1、对于会改变程序执行结果的重排序,JMM要求编译器和处理器必须禁止这种重排序。

    2、对于不会改变程序执行结果的重排序,JMM对编译器和处理器不做要求(JMM允许这种重排序)。

    JMM向程序员提供的happens-before规则能满足程序员的需求。JMM的happens-before规则不但简单易懂,而且也向程序员提供了足够强的内存可见性保证(有些内存可见性保证其实并不一定真实存在,比如上面的A happens-before B)。

    JMM对编译器和处理器的束缚已经尽可能少。JMM其实在遵守一个基本原则:只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都可以。

  1.2 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对编译器和处理器重排序的约束原则。正如前面所言,JMM其实是在遵守一个基本原则:只要不改变程序的执行结果,编译器和处理器怎么优化都行。JMM这么做的原因是:程序员对于这两个操作是否真的是被重排序并不关心,程序员关心的是程序执行时的语义不能被改变,即执行结果不能被改变。因此,happens-before关系本质上和as-if-serial语义是一回事。

    as-if-serial语义保证单线程内程序的执行结果不被改变,happens-bofore关系保证正确同步的多线程程序的执行结果不被改变。

    as-if-serial语义给编写单线程程序的程序员创造了一个幻境:单线程程序是按程序的顺序来执行的。happens-before关系给编写正确同步的多线程程序的程序员创造了一个幻境:正确同步的多线程程序是按happens-before指定的顺序来执行的。

    as-if-serial语义和happens-before这么做的目的,都是为了在不改变程序执行结果的前提下,尽可能地提高程序执行的并行度。

  1.3 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()操作成功返回。

二、双重检查锁定与延迟初始化

   略

三、Java内存模型综述

  3.1 各种内存模型之间的关系

    JMM是一个语言级的内存模型,处理器内存模型是硬件级的内存模型,顺序一致性内存模型是一个理论参考模型。常见的处理器内存模型要比常见的语言级内存模型弱,处理器内存模型和语言级内存模型都要比顺序一致性内存模型弱。同处理器内存模型一样,越是追求执行性能的语言,内存模型设计的会越弱。

  3.2 JMM的内存可见性保证

    按程序类型,Java程序的内存可见性保证可以分为下列3类。

    1、单线程程序。单线程程序不会出现内存可见性问题。编译器、runtime和处理器会共同确保单线程程序的执行结果与该程序在顺序一致性模型中的执行结果相同。

    2、正确同步的多线程程序。正确同步的多线程程序的执行将具有顺序一致性(程序的执行结果与该程序在顺序一致性内存模型中的执行结果相同)。这是JMM关注的重点,JMM通过限制编译器和处理器的重排序来为程序员提供内存可见性保证。

    3、未同步/未正确同步的多线程程序。JMM为他们提供了最小安全性保障;线程执行时读取到的值,要么是之前某个线程写入的值,要么是默认值(0,null,false)。

  3.3 JSR-133对旧内存模型的修补

    JSR-133对JDK 5之前的旧内存模型的修补主要有两个:

    1、增强volatile的内存语义。旧内存模型允许volatile变量与普通变量重排序。JSR-133严格限制volatile变量与普通变量的重排序,使volatile的写-读和锁的释放-获取具有相同的内存语义。

    2、增强final的内存语义。在旧内存模型中,多次读取同一个final变量的值可能会不相同。为此,JSR-133为final增加了两个重排序规则。在保证final引用不会从构造函数内逸出的情况下,final具有了初始化安全性。

总结

    本章对Java内存模型做了比较全面的解读,有助于解决在Java并发编程中经常遇到的各种内存可见性问题。

(第三章)Java内存模型(下)的更多相关文章

  1. 第三章 Java内存模型(下)

    锁的内存语义 中所周知,锁可以让临界区互斥执行.这里将介绍锁的另一个同样重要但常常被忽视的功能:锁的内存语义 锁的释放-获取建立的happens-before关系 锁是Java并发编程中最重要的同步机 ...

  2. (第三章)Java内存模型(上)

    一.java内存模型的基础 1.1 并发编程模型的两个关键问题 在并发编程中,需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来 ...

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

    一.volatile的内存语义 1.1 volatile的特性 理解volatile特性的一个好办法是把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步.下面通过具体 ...

  4. 第三章 Java内存模型(上)

    本章大致分为4部分: Java内存模型的基础:主要介绍内存模型相关的基本概念 Java内存模型中的顺序一致性:主要介绍重排序和顺序一致性内存模型 同步原语:主要介绍3个同步原语(synchroized ...

  5. 《深入理解Java虚拟机》-----第12章 Java内存模型与线程

    概述 多任务处理在现代计算机操作系统中几乎已是一项必备的功能了.在许多情况下,让计算机同时去做几件事情,不仅是因为计算机的运算能力强大了,还有一个很重要的原因是计算机的运算速度与它的存储和通信子系统速 ...

  6. 深入理解java虚拟机-第12章Java内存模型与线程

    第12章 Java内存模型与线程 Java内存模型  主内存与工作内存: java内存模型规定了所有的变量都在主内存中,每条线程还有自己的工作内存. 工作内存中保存了该线程使用的主内存副本拷贝,线程对 ...

  7. java并发编程实战:第十六章----Java内存模型

    一.什么是内存模型,为什么要使用它 如果缺少同步,那么将会有许多因素使得线程无法立即甚至永远看到一个线程的操作结果 编译器把变量保存在本地寄存器而不是内存中 编译器中生成的指令顺序,可以与源代码中的顺 ...

  8. 并发之初章Java内存模型

    >>>>>>博客地址<<<<<< >>>>>>首发博客<<<<< ...

  9. JVM系列(三)— Java内存模型

    我们已经了解了Java虚拟机的运行时数据区,垃圾收集相关知识,接下来学习虚拟机非常重要的部分 这就是Java内存模型与线程(第12章),这一篇,将主要讲讲内存模型 了解Java内存模型之前,先了解下计 ...

随机推荐

  1. Nginx配置域名跳转实例

    要求:浏览器地址栏输入qj.123.com之后,地址自动变成qj.abc.com 配置nginx跳转 server { listen 80; server_name qj.abc.com qj.123 ...

  2. [!] Unable to satisfy the following requirements:

    出现这个问题是由于我本地Podfile文件上第三方版本太低. 解决方案就是,更新一下本地Podfile文件上的第三方版本,也就是pod update --verbose一下. 注意一下,这个命令需要很 ...

  3. (一)Angularjs - 入门

    AngularJS进行应用开发的一个重要的思维模式: 从构造声明式界面入手 ng-app: 这个指定定义并且关联了使用angularJS的HTML页面部分 ng-model: 这个指定定义并绑定Ang ...

  4. 学习开发jquery插件

    先学习http://www.cnblogs.com/playerlife/archive/2012/05/11/2495269.html http://www.cnblogs.com/fromeart ...

  5. php设计模式 1单例模式

    之前很长时间之前就学习过设计模式,但是因为在实践中很少应用,所以忽略了,但现在却意识到设计模式很重要的,程序设计简介高效冗余性代码少. 今天开始把前几天学习的几个设计模式整理一下,首先当然是单例模式. ...

  6. 客户端是选择Java Swing还是C# Winform

      登录|注册     mentat的专栏       目录视图 摘要视图 订阅 [专家问答]韦玮:Python基础编程实战专题     [知识库]Swift资源大集合    [公告]博客新皮肤上线啦 ...

  7. MVC + LigerUI 做后台管理还真是清爽

    LigerUI是基于Jquery,轻量级UI框架.具体可以看官方演示 http://www.ligerui.com/ 我的简单后台 模拟Winodw桌面效果,挺不错呢.最喜欢的还是他的,下拉列表绑定G ...

  8. INNO setup 制作安装包

    1.获取SQLserver安装路径vardbpath:string;rtn:boolean;rtn := RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWA ...

  9. IE 将“通过域访问数据源”设置为启用(注册表)

    XP HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1\1406 Vista+ HKCU\Softwar ...

  10. Linq中join & group join & left join 的用法

    Linq中join & group join & left join 的用法 2013-01-30 11:12 12154人阅读 评论(0) 收藏 举报  分类: C#(14)  文章 ...