单例模式的DCL是一种比较好的单例实现方式,面试中被问及的频率非常高,考察的方式也多种多样。这里简单整理了一下,这里面的每一个点最好都能够做到烂熟于心:

 1 public class Test {
2 private volatile static Test instance;
3
4 private Test() {
5
6 }
7
8 public static Test getInstance() {
9 if (instance == null) {
10 synchronized (Test.class) {
11 if (instance == null) {
12 instance = new Test();
13 }
14 }
15 }
16 return instance;
17 }
18 }

这里有5个要点需要注意:

(1)第一个注意点:使用私有的构造函数,确保正常情况下该类不能被外部初始化(非正常情况比如通过反射初始化,一般使用反射之后单例模式也就失去效果了)。

(2)第二个注意点:getInstance方法中第一个判空条件,逻辑上是可以去除的,去除之后并不影响单例的正确性,但是去除之后效率低。因为去掉之后,不管instance是否已经初始化,都会进行synchronized操作,而synchronized是一个重操作消耗性能。加上之后,如果已经初始化直接返回结果,不会进行synchronized操作。

(3)第三个注意点:加上synchronized是为了防止多个线程同时调用getInstance方法时,各初始化instance一遍的并发问题。

(4)第四个注意点:getInstance方法中的第二个判空条件是不可以去除,如果去除了,并且刚好有两个线程a和b都通过了第一个判空条件。此时假设a先获得锁,进入synchronized的代码块,初始化instance,a释放锁。接着b获得锁,进入synchronized的代码块,也直接初始化instance,instance被初始化多遍不符合单例模式的要求~。加上第二个判空条件之后,b获得锁进入synchronized的代码块,此时instance不为空,不执行初始化操作。

(5)第五个注意点:instance的声明有一个voliate关键字,如果不用该关键字,有可能会出现异常。因为instance = new Test();并不是一个原子操作,会被编译成三条指令,如下所示。
          1)给Test的实例分配内存

2)初始化Test的构造器

3)将instance对象指向分配的内存空间(注意,此时instance就不为空)

然后咧,java会指令重排序,JVM根据处理器的特性,充分利用多级缓存,多核等进行适当的指令重排序,使程序在保证业务运行的同时,充分利用CPU的执行特点,最大的发挥机器的性能!简单来说就是jvm执行上面三条指令的时候,不一定是1-2-3这样执行,有可能是1-3-2这样执行。如果jvm是按照1-3-2来执行的话,当1-3执行完2还没执行的时候,如果另外一个线程调用getInstance(),因为3执行了此时instance不为空,直接返回instance。问题是2还没执行,此时instance相当于什么都没有,肯定是有问题的。然后咧,voliate有一个特性就是禁止指令重排序,上面的三条指令是按照1-2-3执行的,这样就没有问题了。

参考:https://blog.csdn.net/hnd978142833/article/details/81633730

最新文章

  1. vue2.0实践的一些细节
  2. js 预处理用户上传图片
  3. django xadmin多个model的数据渲染在统一个template中
  4. Hadoop实战2:MapReduce编程-WordCount实例-streaming-python环境
  5. PHP 堆排序实现
  6. ASP.NET MVC5学习笔记之Action参数模型绑定基本过程
  7. 跨平台音乐播放器qmmp(Cross-Platform Audio Player Qmmp)
  8. Android 滑动效果高级篇(八)—— 自定义控件
  9. OpenMP多线程linux下的使用,简单化
  10. 【转载】最近在用Arrays的asList()生成的List时,List元素的个数时而不正确,数组转化为List,即Arrays.asList(intArray);
  11. CentOS下FTP服务器安装与配置
  12. Netty IO线程模型学习总结
  13. paper资料
  14. Angular - - ngRoute Angular自带的路由
  15. jQuery EasyUI弹出确认对话框(确认操作中.....)
  16. 【ASP.NET Core】EF Core - “影子属性”
  17. MySQL使用AUTO_INCREMENT列的表注意事项之update自增列篇
  18. 3.7 C++派生类构造函数调用规则
  19. uva-270-排序
  20. Linux下Nagios的安装与配置 及遇到的坑

热门文章

  1. Pyqt5_QMessageBox
  2. java——定时任务
  3. go语言依赖注入实现
  4. Linux,Mac下MySQL的安装及一些知识点的整理
  5. 【JVM】体系结构及其细节
  6. StackOverflow 创始人关于如何高效编程的清单.md
  7. 【已解决】Ubuntu U盘启动出现“Failed to load ldlinux.c32”问题
  8. Beta冲刺 —— 5.30
  9. Java实现 LeetCode 834 树中距离之和(DFS+分析)
  10. Java 第十一届 蓝桥杯 省模拟赛 第十层的二叉树