培养能力的事必须持续一直地去做,又必须随时改善学习办法,进步学习效率,才会胜利。
单例模式的定义:保障一个类仅有一个实例,并提供一个拜访它的全局拜访点!
懒汉
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; }}