单例:静态内部类和DCL实现

1.DCL

/**
 * JVM为了优化指令,提高程序运行效率,允许指令重排序。
 * 1)给instance实例分配内存;
 * 2)初始化instance的构造器;
 * 3)将instance对象指向分配的内存空间(注意到这步时instance就非null了)
 * 指令重排序导致:可能2)初始化instance未完成就执行3)了
 */
public class DCLSingleInstance {
    //1.volatile保证有序性(禁止指令重排序),确保instance可见性(每次均在主内存中读取)
    private static volatile DCLSingleInstance instance;

    private DCLSingleInstance() {
    }

    public static DCLSingleInstance getInstance() {
        //2.synchronized不加在方法上避免每次调用getInstance方法时都进行同步准备(耗时,性能低)
        //3.第一次判断==null了避免非必要加锁,第二次==null才去实例化
        if (instance == null) {
            synchronized (DCLSingleInstance.class) {
                if (instance == null) {
                    instance = new DCLSingleInstance();
                }
            }
        }
        return instance;
    }
}

2.静态内部类

先了解一下java内部类及类加载顺序:
变量定义的先后顺序决定初始化顺序,而在不同变量之间,又存在着某些规则(先静态对象,再非静态对象)
静态代码块属于类,且在类加载时只执行一次,即使后面有类加载的条件也不会再次执行;

类加载条件:
1 创建类的实例
2 访问某个类或接口的静态变量,或者对该静态变量赋值
3 调用类的静态方法
4 反射(如Class.forName(""))
5 初始化一个类的子类,它的父类也会被初始化
6 java虚拟机启动时被表明为启动类的类(java test)
7 jdk1.7开始提供的动态语言 支持 :java.lang.invoke.MethodHandle实例的解析结果REF——getSatic, REF_putStatic,REF_invokeStatic 句柄对应的类没有初始化,则初始化

内部类的加载: 内部类是延时加载的,也就是说只会在第一次使用时加载。不使用就不加载。

/**
 * 优点:当外部类第一次被加载时,不会去加载内部类,只有当getInstance()方法第一次被调用时,
 * 才会去加载内部类从而初始化instance,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
 * 缺点:传参的问题(由于是静态内部类的形式去创建单例的,故外部无法传递参数进去,例如Context这种参数,
 * 所以创建单例时,可以在静态内部类与DCL模式里自己斟酌)
 */
public class StaticInnerSingleInstance {
    private StaticInnerSingleInstance() {
    }

    private static class Holder {
        private static final StaticInnerSingleInstance instance = new StaticInnerSingleInstance();
    }

    public static StaticInnerSingleInstance getInstance() {
        return Holder.instance;
    }
}

你可能感兴趣的:(单例:静态内部类和DCL实现)