原文地址https://www.hcyhj.cn/2018/11/21/delay-load


最近开始看《java并发编程的艺术》一书,从里面get到了好些知识上的盲点,下面就延迟加载这个问题来分析一波~~


首先咱们来看一段简单的代码:

public class DelayLoad {private DelayLoad() {}private static DelayLoad instance;public static DelayLoad getInstance() {if (instance == null) {               //步骤1instance = new DelayLoad();       //步骤2}return instance;}
}

从上面的代码片段里,很容易发现在多线程并发情况下去调用getInstance是会出问题的.当A线程和B线程同时进入到步骤1处,便会实例化两个对象出来,A和B访问到的对象就不会是同一个。


下面升级一下,加上同步关键字synchronized

public class DelayLoad {private DelayLoad() {}private static DelayLoad instance;public static synchronized DelayLoad getInstance() {if (instance == null) {instance = new DelayLoad();}return instance;}
}

代码改成这样后,可以完全保证并发情况下获取的instance实例都会是同一个,但是多个线程同时调用synchronized 修饰的方法,会有获取锁以及释放锁操作,这里会造成大量的性能损耗,得不偿失!


继续改造一下,看能不能提升下性能:

public class DelayLoad {private DelayLoad() {}private static DelayLoad instance;public static  DelayLoad getInstance() {if (instance == null) {                     //第一次检查synchronized (DelayLoad.class){if (instance == null) {             //第二次检查instance = new DelayLoad();    //创建实例}}}return instance;}
}

咱们这里用双重检测的方法来实现这个单例懒加载,用这种策略看上去貌似没有什么问题,多线程并发的情况下往往也就是在第一次检查时都会直接返回实例,这样就不会造成性能损耗.但是,这里有可能出现instance不一致的问题。对于这个问题我们得先了解对象的初始化过程

对象的初始化过程

1.在堆上为DelayLoad对象分配足够大的空间,所有属性和方法都被设置成缺省值(数字为0,字符为null,布尔为false,而所有引用被设置成null)。
2.执行构造函数检查是否有父类,如果有父类会先调用父类的构造函数,这里假设DelayLoad没有父类,执行缺省值字段的赋值即方法的初始化动作。
3.执行构造函数.

上面创建实例的那一步在cpu上可能经过如下操作:

memory = allocate(); //1分配对象内存空间
initInstance(memory);//2初始化对象
instance = memory;  //3设置instance指向刚分配的内存地址

但实际上执行的过程中,2和3步骤有可能进行指令重排,也就是按132的顺序执行,这样就会导致instance指向的是一个属性和值都是缺省值的对象。然后被一个竞争线程所拿到并进行使用。


目前有两种解决办法

第一种:给实例变量加上volatile 关键字修饰

public class DelayLoad {private DelayLoad() {}private static volatile DelayLoad instance;public static  DelayLoad getInstance() {if (instance == null) {synchronized (DelayLoad.class){if (instance == null) {instance = new DelayLoad();}}}return instance;}
}

代码改成上述情况后,在设置instance指向更分配的内存地址之前会有StoreStore内存屏障,执行代码会禁止指令重排,这样咱们拿到的instance都是经过初始化过的。

第二种:基于类初始化的解决方案

public class DelayLoad {private DelayLoad() {}private static class DelayLoadHolder {public static DelayLoad instance = new DelayLoad();}public static DelayLoad getInstance() {return DelayLoadHolder .instance;}
}

该延迟加载方案是基于JVM的类初始化原理实现的。在执行类的初始化期间,JVM会去获取一个锁,该锁可以同步多个线程对同一个类的初始化。类只会被加载一次,在加载完成之前对其他线程都是不可见的。这样也能保证获取到的instance也是同一个。


End

转载于:https://www.cnblogs.com/itjun/p/9994367.html

延迟加载的一些知识和误区相关推荐

  1. 考试行测常识判断法律知识备考误区提示

    行测常识判断考查范围比较广泛,重点分布不均匀,在公务员考试中属于较难抓分的部分,因此很多考生对于此处知识都处于放弃的状态.事实上,掌握常识学习的方法,拿分也并不困难.为此,中公教育特对法律常识备考时存 ...

  2. 计算机考研逻辑学,管综专硕:走出不考逻辑学专业知识的误区

    每年大纲解读的时候,包括很多逻辑老师在讲解MBA.MPA.MPAcc课程的时候,总是告诉学员或者考生们,大纲明确说明不考查逻辑学的专业知识.由此得出结论,不考逻辑学的专业知识,主要考考生对各种信息的理 ...

  3. 开发环境和运行环境的区别_生产环境 VS 开发环境,关于Kubernetes的四大认识误区...

    来源:http://t.cn/ExaHoL2 最近我们澄清了一些大家在进行Kubernetes实验的时候所见到的常见的误解.其中最大的一个误解就是:在生产环境中运行Kubernetes和开发测试环境并 ...

  4. 构建知识体系(1):知识体系是什么?

    黄金圈思维思考法则: 第一个层面是what, 也就是事情的表象, 我们具体做的每一件事: 第二个层面是how, 也就是我们如何实现我们想要做的事情: 第三个层面是why, 也就是我们为什么做这样的事情 ...

  5. 深入理解 WIN32 PE 文件格式

    原文地址:http://www.cppblog.com/shaoxie1986/articles/126142.html 译自:An In-Depth Look into the Win32 Port ...

  6. 抖音短视频庞大的流量池,新手小白如何杀出一条血路

    面对抖音短视频庞大的流量池和众多头部大号漂亮数据的诱惑,绝大多数普通人在建立账号之初都会有一夜爆红.日涨万粉的憧憬.但是实际上,在抖音短视频平台逐渐成熟的今天,越来越多优质内容出现,要想在这里杀出一条 ...

  7. 如何制定医院病区6S管理考核标准?

    华天谋6s管理专家概述:在每一次开展6S项目前,专家都会对医院全体员工进行6S培训,普及6S基本知识和误区盲点,使医院广大员工知晓开展6S活动的好处,从而主动去落实6S现场管理. 那么,医院病区该如何 ...

  8. 如何用三年时间获得十年工作经验?

    在如今的工作和生活中,"天下武功,唯快不破"成了我们的追求目标.我们都希望在短期内速成,什么"21天成就xxx"."xxx技能100天速成" ...

  9. 广州品茗:此间普洱香四溢

    广州品茗:此间普洱香四溢 在各种茶叶中,普洱颇有长者之风.它温厚.醇和,愈陈愈香.茶界有这样的说法:云南产普洱,香港存普洱,台湾留普洱.懂得收藏和鉴赏普洱的行家,以往多集中在香港和台湾,近年,广州的普 ...

最新文章

  1. LeetCode 25. K 个一组翻转链表
  2. 如何在Linux使用Eclipse + CDT开发C/C++程序 OS Linux C/C++ gcc
  3. CD管理和检索软件比较
  4. postgres 错误duplicate key value violates unique constraint 解决方案
  5. HDU 4868 Information Extraction(2014 多校联合第一场 H)
  6. 【今日头条】【抖音火山】前端开发实习生
  7. 系统架构师学习笔记-软件架构设计
  8. react-native Android release打包失败
  9. SSM - 全局跨域处理
  10. 使用UltraISO制作LINUX安装U盘(未成功)
  11. 第25章 串行FLASH文件系统FatFs—零死角玩转STM32-F429系列
  12. Oracle数据库 1653错误
  13. Ionic4 Ionic-refresher 下拉更新
  14. JavaDoc文档生成
  15. 【navicat】定时清除数据库备份,并保留最近7天
  16. 计算机应用基础学习网站,《计算机应用基础》学习手册.pdf
  17. MT6261芯片设计最新参考资料分享
  18. 用ubuntu的使用习惯使用windows (by quqi99)
  19. java 获取下一年_JAVA获取下一年,下个月,下一天;月份为何以0开始?
  20. Mysql实战之快速填充序列维度表

热门文章

  1. Alphabet高管:移动搜索为收入主要增长点 广告形式改进长期获益
  2. ++i 和 i++ 性能上的区别
  3. Enterprise Solution 进销存管理软件 C/S架构,支持64位系统 物流,资金流,信息流全面集成...
  4. 字符编码笔记:ASCII,Unicode和UTF-8(转)
  5. 因服务器配置不当,热门直播平台 Twitch 的125GB 数据和源代码被泄露
  6. CloudFlare CDNJS 漏洞差点造成大规模的供应链攻击
  7. Linux学习总结(二十九)系统日志
  8. Oracle 11g EM删除重建的方法
  9. Codeforces 474 C. Captain Marmot
  10. 设计模式(十)——代理模式