一 引言

  说到final你肯定知道它是Java中的关键字,那么它所在Java中的作用你知道吗?不知道的话,请前往这篇了解下https://www.cnblogs.com/yuanfy008/p/8021673.html

  今天我们来说说final域在JMM中的内存语义。

二 final域的重排序规则

  开门见山,对于final域,编译器和处理器一定要遵守两个重排序规则(JSR-133才增强了final域):

  1)在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这个两个操作不能被重排序。

  2)初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。

  下面我们通过案例来说明这两点(假设线程1执行writer(),随后另一个线程执行reader()方法):

public class FinalExample {
static volatile boolean flag = true;
int i = 0;
final int j;
static FinalExample obj; public FinalExample() { // 构造函数
i = 1; // 写普通域
j = 2; // 写final域
} public static void writer() { // 线程1写入
obj = new FinalExample();
} public static void reader() { // 线程2读取
FinalExample example = obj; // 读对象引用
System.out.println(example.i); // 读普通域
System.out.println(example.j); // 读final域
}
}

  写final域的重排序规则禁止把final域的写重排序到构造函数之外。这个规则的实现包含下面两个方面:

  1)JMM禁止编译器吧final域的写重排序到构造函数之外。

  2)编译器会在final域的写之后,构造函数return之前,插入一个StoreStore屏障。这个屏障禁止处理器把final域的写重排序到构造函数之外。

  所以线程1执行顺序如下图(其中写普通域的顺序无法保证,理论上是存在下面三种情况的,要想验证普通域是否有重排序的结果有点难,因为无法保证线程1把普通域重排序后,线程2能够读取它之前的0值):

  读final域的重排序规则是:在一个线程中,初次读这个对象引用与初次读该对象包含的final域,JMM禁止处理器重排序这两个操作。其中编译器会在读final域操作的前面插入一个LoadLoad屏障。由于插入了loadLoad屏障,读普通域i的操作是不会重排序到读final域,但是不保证它会重排到读对象引用这个操作的前面。所以线程2的一个执行顺序就能想象到了,这里就不画线程2的执行顺序图了。 

三 final域为引用类型

  如果finaly域为引用类型,JMM中是怎么处理的呢?对于引用类型,写final域的重排序规则对编译器和处理器增加了如下约束:在构造函数内对一个final引用的对象的成员域的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。 

       以上及其以上都要注意:只针对于构造函数方法内。另外要想以上规则确保,还需要一个条件:在构造函数内部,不能让这个被构造对象的引用被其他线程可见,也就是对应引用不能再构造函数中“逸出”。如下案例:

  

class FinalReferenceEscapeExample {
final int i;
static FinalReferenceEscapeExample obj; public FinalReferenceEscapeExample() {
i = 1; // 1
obj = this; // 2 this引用逸出
} public static void writer() { // 线程1
new FinalReferenceEscapeExample();
} public static void reader() { // 线程2
if (obj != null) {
System.out.println(obj.i);
}
}
}

  上面程序,第一步写final域与第二步是不保证重排序的。所以当第一步与第二步重排之后,线程1执行完这步(obj = this)后,时间片分给第二个线程执行,那么线程2将会获取final域初始化之前的值,这肯定就违背了程序的初衷。

  

Java内存模型-final域的内存语义的更多相关文章

  1. Java内存模型-final域的内存语义--没明白,预留以后继续理解

    https://www.cnblogs.com/yuanfy008/p/9349275.html 来自 Java并发编程(1)-Java内存模型

  2. final域的内存语义

    final 一.final的基本语义 final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量) 当用final修饰一个类时,表明这个类不能被继承. 当用final修饰一个方法时,表明这个方 ...

  3. java内存模型7-处理器内存模型

    处理器内存模型 顺序一致性内存模型是一个理论参考模型,JMM和处理器内存模型在设计时通常会把顺序一致性内存模型作为参照.JMM和处理器内存模型在设计时会对顺序一致性模型做一些放松,因为如果完全按照顺序 ...

  4. 深入理解 Java 内存模型(一)- 内存模型介绍

    深入理解 Java 内存模型(一)- 内存模型介绍 深入理解 Java 内存模型(二)- happens-before 规则 深入理解 Java 内存模型(三)- volatile 语义 深入理解 J ...

  5. java线程内存模型,线程、工作内存、主内存

    转自:http://rainyear.iteye.com/blog/1734311 java线程内存模型 线程.工作内存.主内存三者之间的交互关系图: key edeas 所有线程共享主内存 每个线程 ...

  6. Java并发编程之final域的内存语义

    一.final域的重排序规则 对于final域,编译器和处理器要遵循两个重拍序规则: 1.在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序 ...

  7. Java并发编程原理与实战四十四:final域的内存语义

    一.final域的重排序规则 对于final域,编译器和处理器要遵循两个重拍序规则: 1.在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序 ...

  8. Java内存模型(MESI、内存屏障、volatile和锁及final内存语义)

    JMM (Java内存模型) Java线程的实现 实现线程主要有三种方式,Java线程从JDK1.3后采用第一种方式实现: 使用内核线程实现(1:1实现) 使用用户线程实现(1:N实现) 使用用户线程 ...

  9. java内存模型-final

    与前面介绍的锁和 volatile 相比较,对 final 域的读和写更像是普通的变量访问.对于final 域,编译器和处理器要遵守两个重排序规则: 在构造函数内对一个 final 域的写入,与随后把 ...

随机推荐

  1. c#版flappybird 未完全实现

    这些天开始在深圳找工作,想着把从前有些淡忘的技术再温故下.看到尊敬的<传智播客>有一期公开课,讲的是用c#编写flappybird小游戏,也就自己搜了下游戏资源,也来试试看. 其实用到的技 ...

  2. “数学口袋精灵”App的第一个Sprint计划----开发日记

    “数学口袋精灵”第一个Sprint计划----第一天 项目进度: 1.我们在商量这我们的初步想法,考虑要选择做算数的软件还是做关于摄影O2O APP的开发(推荐).每个人会去上网百度浏览了解这两个项目 ...

  3. week9:个人博客作业

    团队作业(5) 以下内容多数是网上的内容,只是做了整合的过程. 要求 在PM 带领下, 每个团队深入分析下面行业的App, 找到行业的Top 5 (从下面的三个备选中,任选一个行业即可) 英语学习/词 ...

  4. Week1个人作业

    关于教材的疑问 阅读的教材<构建之法> 1第一章中提到:“软件企业=软件+商业模式”,这样的结构是否过于简单,尤其是在互联网+时代 2.在进行单元测试的时候,怎么做到100%覆盖 3.个人 ...

  5. 牛B的VUE讲解

    https://www.cnblogs.com/lvhw/p/8286544.html

  6. EntityFramework异常The specified cast from a materialized 'System.Double' type to the 'System.Single' type is not valid.

    实体类: public class ReportEntity { public string FactorName { get; set; } public double MaxVal { get; ...

  7. 使用fiddler的过滤条件

    使用fiddler抓包的时候经常一下子显示很多的记录,看的眼花缭乱,需要这时候需要使用过滤条件来帮助你,一般常用的有三种过滤条件: 1.域名过滤,只显示特定域名的记录: *.baidu.com表示所有 ...

  8. 2017-12 CDQZ集训(已完结)

    从联赛活了下来(虽然分数倒一……),接下来要去CDQZ集训啦…… DAY -2 2017-12-16 被老师安排负责一部分同学的住宿以及安排…… 抓紧时间继续学习,LCT真好玩啊真好玩…… 晚上放假了 ...

  9. MT【167】反复放缩

    已知数列$\{a_n\}$满足:$a_1=1,a_{n+1}=a_n+\dfrac{a_n^2}{n(n+1)}$1)证明:对任意$n\in N^+,a_n<5$2)证明:不存在$M\le4$, ...

  10. 【刷题】BZOJ 4195 [Noi2015]程序自动分析

    Description 在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足. 考虑一个约束满足问题的简化版本:假设x1,x2,x3,-代表程序中出现的变量,给定n个形如xi=xj或x ...