培养能力的事必须持续一直地去做,又必须随时改善学习办法,进步学习效率,才会胜利。
单例模式的定义:保障一个类仅有一个实例,并提供一个拜访它的全局拜访点!
懒汉
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种写法可能在多线程中很好的工作,而且看起来它也具备很好的lazy loading,然而,遗憾的是,效率很低,99%状况下不须要同步。
饿汉
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
这种形式基于classloder机制防止了多线程的同步问题,instance在类装载时就实例化。目前java单例是指一个虚拟机的范畴,因为装载类的性能是虚拟机的,所以一个虚拟机在通过本人的ClassLoader装载饿汉式实现单例类的时候就会创立一个类的实例。
这就意味着一个虚拟机外面有很多ClassLoader,而这些Classloader都能装载某个类的话,就算这个类是单例,也能产生很多实例。当然如果一台机器上有很多虚拟机,那么每个虚拟机中都有至多一个这个类的实例的话,那这样就更不会是单例了。(这里探讨的单例不适宜集群!)
动态外部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种形式同样利用了classloder的机制来保障初始化instance时只有一个线程,这种形式是Singleton类被装载了,instance不肯定被初始化。因为SingletonHolder类没有被被动应用,只有显示通过调用getInstance办法时,才会显示装载SingletonHolder类,从而实例化instance。
设想一下,如果实例化instance很耗费资源,我想让他提早加载!这个时候,这种形式相比第2种形式就显得很正当。
枚举
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
这种形式是Effective Java的作者Josh Bloch提倡的形式,它不仅能防止多线程同步问题,而且还能避免反序列化从新创立新的对象,不过,集体认为因为1.5中才退出enum个性,用这种形式写未免让人感觉陌生,在理论工作中,也很少看见有人这么写过。
双重校验锁(jdk1.5)
public class Singleton {
private volatile static Singleton singleton;
private Singleton() {}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
这样形式实现线程平安地创立实例,而又不会对性能造成太大影响。它只是第一次创立实例的时候同步,当前就不须要同步了。
因为volatile关键字屏蔽了虚拟机中一些必要的代码优化,所以运行效率并不是很高,因而倡议没有特地的须要不要应用。双重测验锁形式的单例不倡议大量应用,依据状况决定。
总结
有两个问题须要留神:
- 如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假设不是远端存取,例如一些servlet容器对每个servlet应用齐全不同的类装载器,这样的话如果有两个servlet拜访一个单例类,它们就都会有各自的实例。
- 如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和还原。不管怎样,如果你序列化一个单例类的对象,接下来还原多个那个对象,那你就会有多个单例类的实例。
对第一个问题修复的方法是:
private static Class getClass(String classname)
throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
对第二个问题修复的方法是:
public class Singleton implements java.io.Serializable {
public static Singleton INSTANCE = new Singleton();
protected Singleton() {
}
private Object readResolve() {
return INSTANCE;
}
}
发表回复