DCL
dcl 实现的单例模式的一种可靠方式是添加 volatile关键字 。
volatile 有如下的特点:
- 有序性,防止重排序
- 可见性 ,屏蔽local cache,变量的操作对于主存立即可见
在java中,对象的创建有如下的几个步骤
- 分配对象内存空间
- 初始化对象
- 设置instance指向刚刚分配的内存地址, 此时instance != null (重点)
只有这三个步骤完成了,才算是一个对象的初始化完成。
计算机指令 CPU 和编译器为了提高执行效率,会对代码进行优化 即重排序 ,只要最终的输出结果是正确的即可。
那么在对象创建的过程中,有可能编程了如下的顺序
- 分配对象内存空间
- 设置instance指向刚刚分配的内存地址, 此时instance != null (重点)
- 初始化对象
当第一个线程拿到锁并且进入到第二个if方法后, 先分配对象内存空间, 然后再instance指向刚刚分配的内存地址, instance 已经不等于null, 但此时instance还没有初始化完成。如果这个时候又有一个线程来调用getInstance方法, 在第一个if的判断结果就为false, 于是直接返回还没有初始化完成的instance, 那么就很有可能产生异常。
1 | public class Singleton { |
在new关键字背后,有如下步骤与
在堆内存中创建对象实例
为实例对象赋初值
返回对象引用
静态内部类模式
1 | public class CustomManager{ |
静态内部类的原理是:
当 SingleTon 第一次被加载时,并不需要去加载 SingleTonHoler,只有当 getInstance() 方法第一次被调用时,才会去初始化 INSTANCE,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。getInstance 方法并没有多次去 new 对象,取的都是同一个 INSTANCE 对象。
虚拟机会保证一个类的
缺点在于无法传递参数,如Context等