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;
}
}
网友评论