单例模式: 确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例。bash
避免产生多个对象消耗过多的资源,或者某种类型的对象只有一个。函数
public static class Singleton {
private static final instance Singleton = new Singleton();
private Singleton() { // 私有构造函数
}
public static Singleton getInstance() {
return instance;
}
}
复制代码
instance是静态成员变量,在声明时就被实例化。优化
public static class Singleton {
private static final instance Singleton = null;
private Singleton() { // 私有构造函数
}
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton()
}
return instance;
}
}
复制代码
instance只是在初次调用时初始化,synchronized,每次调用getInstance()方法都会进行同步,这样会消耗没必要要的资源,这也是懒汉模式最大的问题。ui
public static class Singleton {
private static final instance Singleton = null;
private Singleton() { // 私有构造函数
}
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton()
}
}
}
return instance;
}
}
复制代码
DCL有两次判空,第一次判空,是为了不没必要要的同步;第二次判空,是为了初次初始化时,只初始化一次。
spa
instance = new Singletone(); 这行代码最终会编译成多条汇编指令,它大体作了三件事:线程
DCL失效状况:
因为Java编译器容许处理器乱序执行,以及JDK1.5以前JMM(Java Memory Model)中Cache、寄存器到内存回写顺序的规定,上面第二和第三的顺序是没法保证的。也就是说有可能JVM会为新的Singleton实例分配空间,而后直接赋值给instance成员,而后再去初始化这个Singleton实例,这样就可能出错了。咱们以A、B两个线程为例:
code
在JDK1.5以后,SUN公司调整了JVM,增长了volatile关键字,只需将instance的定义改为private volatile static Singleton instance = null就能够保存instance对象每次都是从主内存总读取。对象
public static class Singleton {
private Singleton() { // 私有构造函数
}
private static class SingletonHolder {
private static final instance Singleton = new Singleton();
}
public static synchronized Singleton getInstance() {
return SingletonHolder.instance;
}
}
复制代码
当第一次加载Singleton类时,并不会初始化,只有当第一次调用getInstance()方法,会致使虚拟机加载SingletonHolder类,此时才是初始化。利用的Java类加载机制接口
不一样的类装载器,可能会致使产生多个单例对象:内存
public static Class getClass(String className) throws ClassNotFoundException {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
cl = Singleton.class.getClassLoader();
}
return cl.loadClass(className);
}
复制代码
若是Singleton实现了Serializable接口,反序列化时可能产生多个实例对象:
private Object readResolve() { // 重写readResolve方法
return instance;
}
复制代码