当前位置: 首页 > news >正文

网站建设的可行性研究的前提鹤壁网站建设兼职

网站建设的可行性研究的前提,鹤壁网站建设兼职,公众号网页源码,wordpress 延迟加载插件Java 语言的每个关键字都设计的很巧妙#xff0c;金雕玉琢#xff0c;只有深度钻研其中#xff0c;才知其中懊悔#xff0c;本文带领大家一起深入理解 Java 内存模型之 final。加我微信好友的不要着急#xff0c;手机没电了#xff0c;等我借个充电器之后#xff0c;再一… Java 语言的每个关键字都设计的很巧妙金雕玉琢只有深度钻研其中才知其中懊悔本文带领大家一起深入理解 Java 内存模型之 final。加我微信好友的不要着急手机没电了等我借个充电器之后再一一通过与前面介绍的锁和 volatile 相比较对 final 域的读和写更像是普通的变量访问。对于 final 域编译器和处理器要遵守两个重排序规则在构造函数内对一个 final 域的写入与随后把这个被构造对象的引用赋值给一个引用变量这两个操作之间不能重排序。初次读一个包含 final 域的对象的引用与随后初次读这个 final 域这两个操作之间不能重排序。下面我们通过一些示例性的代码来分别说明这两个规则public class FinalExample {int i; // 普通变量final int j; //final 变量static FinalExample obj;public void FinalExample () { // 构造函数 i 1; // 写普通域 j 2; // 写 final 域}public static void writer () { // 写线程 A 执行 obj new FinalExample ();}public static void reader () { // 读线程 B 执行FinalExample object obj; // 读对象引用int a object.i; // 读普通域int b object.j; // 读 final 域}}这里假设一个线程 A 执行 writer () 方法随后另一个线程 B 执行 reader () 方法。下面我们通过这两个线程的交互来说明这两个规则。# 写 final 域的重排序规则写 final 域的重排序规则禁止把 final 域的写重排序到构造函数之外。这个规则的实现包含下面 2 个方面JMM 禁止编译器把 final 域的写重排序到构造函数之外。编译器会在 final 域的写之后构造函数 return 之前插入一个 StoreStore 屏障。这个屏障禁止处理器把 final 域的写重排序到构造函数之外。现在让我们分析 writer () 方法。writer () 方法只包含一行代码finalExample new FinalExample ()。这行代码包含两个步骤构造一个 FinalExample 类型的对象把这个对象的引用赋值给引用变量 obj。假设线程 B 读对象引用与读对象的成员域之间没有重排序(马上会说明为什么需要这个假设)下图是一种可能的执行时序在上图中写普通域的操作被编译器重排序到了构造函数之外读线程 B 错误的读取了普通变量 i 初始化之前的值。而写 final 域的操作被写 final 域的重排序规则“限定”在了构造函数之内读线程 B 正确的读取了 final 变量初始化之后的值。写 final 域的重排序规则可以确保在对象引用为任意线程可见之前对象的 final 域已经被正确初始化过了而普通域不具有这个保障。以上图为例在读线程 B“看到”对象引用 obj 时很可能 obj 对象还没有构造完成(对普通域 i 的写操作被重排序到构造函数外此时初始值 2 还没有写入普通域 i)。# 读 final 域的重排序规则读 final 域的重排序规则如下在一个线程中初次读对象引用与初次读该对象包含的 final 域JMM 禁止处理器重排序这两个操作(注意这个规则仅仅针对处理器)。编译器会在读 final 域操作的前面插入一个 LoadLoad 屏障。初次读对象引用与初次读该对象包含的 final 域这两个操作之间存在间接依赖关系。由于编译器遵守间接依赖关系因此编译器不会重排序这两个操作。大多数处理器也会遵守间接依赖大多数处理器也不会重排序这两个操作。但有少数处理器允许对存在间接依赖关系的操作做重排序(比如 alpha 处理器)这个规则就是专门用来针对这种处理器。reader() 方法包含三个操作初次读引用变量 obj;初次读引用变量 obj 指向对象的普通域 j。初次读引用变量 obj 指向对象的 final 域 i。现在我们假设写线程 A 没有发生任何重排序同时程序在不遵守间接依赖的处理器上执行下面是一种可能的执行时序在上图中读对象的普通域的操作被处理器重排序到读对象引用之前。读普通域时该域还没有被写线程 A 写入这是一个错误的读取操作。而读 final 域的重排序规则会把读对象 final 域的操作“限定”在读对象引用之后此时该 final 域已经被 A 线程初始化过了这是一个正确的读取操作。读 final 域的重排序规则可以确保在读一个对象的 final 域之前一定会先读包含这个 final 域的对象的引用。在这个示例程序中如果该引用不为 null那么引用对象的 final 域一定已经被 A 线程初始化过了。# 如果 final 域是引用类型上面我们看到的 final 域是基础数据类型下面让我们看看如果 final 域是引用类型将会有什么效果请看下列示例代码public class FinalReferenceExample {final int[] intArray; //final 是引用类型static FinalReferenceExample obj;public FinalReferenceExample () { // 构造函数 intArray new int[1]; //1 intArray[0] 1; //2}public static void writerOne () { // 写线程 A 执行 obj new FinalReferenceExample (); //3}public static void writerTwo () { // 写线程 B 执行 obj.intArray[0] 2; //4}public static void reader () { // 读线程 C 执行if (obj ! null) { //5int temp1 obj.intArray[0]; //6}}}这里 final 域为一个引用类型它引用一个 int 型的数组对象。对于引用类型写 final 域的重排序规则对编译器和处理器增加了如下约束在构造函数内对一个 final 引用的对象的成员域的写入与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量这两个操作之间不能重排序。对上面的示例程序我们假设首先线程 A 执行 writerOne() 方法执行完后线程 B 执行 writerTwo() 方法执行完后线程 C 执行 reader () 方法。下面是一种可能的线程执行时序在上图中1 是对 final 域的写入2 是对这个 final 域引用的对象的成员域的写入3 是把被构造的对象的引用赋值给某个引用变量。这里除了前面提到的 1 不能和 3 重排序外2 和 3 也不能重排序。JMM 可以确保读线程 C 至少能看到写线程 A 在构造函数中对 final 引用对象的成员域的写入。即 C 至少能看到数组下标 0 的值为 1。而写线程 B 对数组元素的写入读线程 C 可能看的到也可能看不到。JMM 不保证线程 B 的写入对读线程 C 可见因为写线程 B 和读线程 C 之间存在数据竞争此时的执行结果不可预知。如果想要确保读线程 C 看到写线程 B 对数组元素的写入写线程 B 和读线程 C 之间需要使用同步原语(lock 或 volatile)来确保内存可见性。# 为什么 final 引用不能从构造函数内“逸出”前面我们提到过写 final 域的重排序规则可以确保在引用变量为任意线程可见之前该引用变量指向的对象的 final 域已经在构造函数中被正确初始化过了。其实要得到这个效果还需要一个保证在构造函数内部不能让这个被构造对象的引用为其他线程可见也就是对象引用不能在构造函数中“逸出”。为了说明问题让我们来看下面示例代码public class FinalReferenceEscapeExample {final int i;static FinalReferenceEscapeExample obj;public FinalReferenceEscapeExample () { i 1; //1 写 final 域 obj this; //2 this 引用在此“逸出”}public static void writer() {new FinalReferenceEscapeExample ();}public static void reader {if (obj ! null) { //3int temp obj.i; //4}}}假设一个线程 A 执行 writer() 方法另一个线程 B 执行 reader() 方法。这里的操作 2 使得对象还未完成构造前就为线程 B 可见。即使这里的操作 2 是构造函数的最后一步且即使在程序中操作 2 排在操作 1 后面执行 read() 方法的线程仍然可能无法看到 final 域被初始化后的值因为这里的操作 1 和操作 2 之间可能被重排序。实际的执行时序可能如下图所示从上图我们可以看出在构造函数返回前被构造对象的引用不能为其他线程可见因为此时的 final 域可能还没有被初始化。在构造函数返回后任意线程都将保证能看到 final 域正确初始化之后的值。# final 语义在处理器中的实现现在我们以 x86 处理器为例说明 final 语义在处理器中的具体实现。上面我们提到写 final 域的重排序规则会要求译编器在 final 域的写之后构造函数 return 之前插入一个 StoreStore 障屏。读 final 域的重排序规则要求编译器在读 final 域的操作前面插入一个 LoadLoad 屏障。由于 x86 处理器不会对写 - 写操作做重排序所以在 x86 处理器中写 final 域需要的 StoreStore 障屏会被省略掉。同样由于 x86 处理器不会对存在间接依赖关系的操作做重排序所以在 x86 处理器中读 final 域需要的 LoadLoad 屏障也会被省略掉。也就是说在 x86 处理器中final 域的读 / 写不会插入任何内存屏障# JSR-133 为什么要增强 final 的语义在旧的 Java 内存模型中 最严重的一个缺陷就是线程可能看到 final 域的值会改变。比如一个线程当前看到一个整形 final 域的值为 0(还未初始化之前的默认值)过一段时间之后这个线程再去读这个 final 域的值时却发现值变为了 1(被某个线程初始化之后的值)。最常见的例子就是在旧的 Java 内存模型中String 的值可能会改变(参考文献 2 中有一个具体的例子感兴趣的读者可以自行参考这里就不赘述了)。为了修补这个漏洞JSR-133 专家组增强了 final 的语义。通过为 final 域增加写和读重排序规则可以为 java 程序员提供初始化安全保证只要对象是正确构造的(被构造对象的引用在构造函数中没有“逸出”)那么不需要使用同步(指 lock 和 volatile 的使用)就可以保证任意线程都能看到这个 final 域在构造函数中被初始化之后的值。编辑业余草来源https://www.xttblog.com/?p4957 往期推荐 ?阿里面试官数据库连接池有必要吗你对它的底层实现了解过没代码,到底该如何分层,才能给人赏心悦目的感觉?你这代码写得真丑满屏的try-catch全局异常处理不会吗 点击
http://www.huolong8.cn/news/400363/

相关文章:

  • 在元典公司做网站有合同吗资讯网站域名选购
  • 天津 网站设计公司南京紫米网络科技有限公司
  • 青岛小型网站建设wordpress程序图片打开慢
  • 淮安公司网站建设漳浦网站开发
  • 东莞建站公司快荐全网天下特别好重庆建设工程信息网官网安全监督信息网
  • 东莞网站改版网上注册公司官网入口
  • 分类网站建设与开发顺企网网址
  • 四川网站营销seo费用广州有哪些建站公司
  • 公司做网站留言板网站建设建站基本流程介绍
  • 做自己照片视频网站wordpress放弃php
  • 北镇建设局网站wordpress 用户 函数
  • 企业网站的建设流程新人写手适合哪个平台
  • 体育网站建设的分析枣阳网站定制
  • 网站备案对应的ip地址手机网址全部出来
  • 青岛建站服务呼叫中心系统电话
  • 网页打不开无法访问此网站烟台网站推广优化
  • 温岭做鞋子的网站江宁网站建设哪家好
  • 网站建设管理教程视频教程软件开发工程师职业满足兴趣因素
  • 小说手机网站建设做招牌的网站
  • 哪个网站做新加坡劳务比较好的郑州网站的优化
  • 收录网站工具怎么做网站凡科
  • 网站不兼容怎么办啊重庆蒲公英网站建设公司怎么样
  • 定制网站建设费用预算山东定制网页建站
  • 什么是网站的后台网站建设合同封面
  • 网站移动终端建设网站兼容性
  • 动漫视频网站模板cms
  • 南宁高端网站建设公司网站改版索引量下降
  • 网站上做网上支付功能网站的统计 怎么做
  • 做网站以后的趋势网线制作一般包括
  • 网站开发的平台做网站点击软件