上篇我们讲述了单例模式的基本概念 , 并且我们也说了饿汉式,懒汉式,线程安全懒汉式 , 但是则三种写法都还不是我推崇的写法 , 今天我们继续说剩下的三种写法 : Dcl写法 ,内部类写法, 枚举式写法 , 其实也可以一篇写完的 , 但是分个章节可以过渡一下 , 这样也可以让我们休息片刻 , 就想电视剧广告更精彩 !
那我们开始 , Let’s Go !
DCL单例模式
DCL 的意思是什么 ? 为什么叫 DCL , 其实就是 Double Check Lock 的简称 , 我们直接看代码 :
public class DclSingleton {
//volatile 保证了原子模型,创建类的步骤顺序不变,指令重排序优化
private static volatile DclSingleton sDclSingleton = null;
private DclSingleton() {
}
public static DclSingleton getInstance() {
//避免不必要的同步请求
if (null == sDclSingleton) {
//保持同步
synchronized (DclSingleton.class) {
//类的初始化操作
if (null == sDclSingleton) {
sDclSingleton=new DclSingleton();
}
}
}
return sDclSingleton;
}
}
这里重点解释一下 , 为什么需要多做一次 null == sDclSingleton 的判断 , 就像注释里边的分析一样 , 因为下边我们用同步代码块进行的同步的操作 , 所以我们在他之前可以进行仅一步的空判断 , 避免不必要的同步请求 .
然后我还需要解释一下为什么对 sDclSingleton 还需要多加一个 volatile 的字段来修饰呢 ? 这个我们可以从类的加载机制来解释 , 第一步 sDclSingleton 需要在方法区中存在一个索引 , 第二步 new DclSingleton(); 的操作的时候我们首先要在堆分配一块控件 , 第三步是将堆中的变量指向索引 , 但是 Jvm 有这么一个缺点 , 在线程不安全的情况下 , 在即时编辑器中存在指令重排序优化 , 导致我们上述所说的步骤可能第三步骤在第二步之前 , 第二步在第一步之前 , 所以也会导致线程不安全 , 我们设置 volative 这个字段就是保持变量的原子性 , 禁止即时编辑器中存在指令重排序优化 , 从而解决线程不安全问题 .
内部类写法
在上边介绍的DCL模式 , 已经解决了进一步的解决了不必要的同步请求 , 但是写法上还是不简单 , 这里我们来介绍一个能满足 延时加载 , 线程安全 , 性能优越 , 写法简单的需求 , 这种写法就是内部类写法 , 如果别人问到你单例模式的写法 , 我推荐用这种写法可以省下不少的说辞.
public class InnerClassSingeton {
private InnerClassSingeton() {
}
private static InnerClassSingeton getInstance() {
return HolderInner.Instance;
}
private static class HolderInner {
public static final InnerClassSingeton Instance = new InnerClassSingeton();
}
}
因为Jvm的本身的机制 static final 修饰的变量能保证唯一性 , 而且静态内部类没有外部方法调用getInstance()方法的时候 , 不会使用内部类 , 保证了延时加载 , 另外我们没有用同步锁的来保证线程安全 ,性能有很大的提升. 所以我推荐这种写法.
枚举类写法
枚举是利用他本身的一个实例 , 这中写法简单 , 但是可能用的少 , 但是需要注意的一点是不能在写额外的获得实例方法 , 这样就不能保证线程的安全了 , 不能再用你Java本身自带的属性了.
public enum EnumSingleton {
//定义一个元素,他就是EnumSingleton的一个实例
INSTANCE;
}