培养能力的事必须持续一直地去做,又必须随时改善学习办法,进步学习效率,才会胜利。

单例模式的定义:保障一个类仅有一个实例,并提供一个拜访它的全局拜访点!

懒汉

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关键字屏蔽了虚拟机中一些必要的代码优化,所以运行效率并不是很高,因而倡议没有特地的须要不要应用。双重测验锁形式的单例不倡议大量应用,依据状况决定。

总结

有两个问题须要留神:

  1. 如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假设不是远端存取,例如一些servlet容器对每个servlet应用齐全不同的类装载器,这样的话如果有两个servlet拜访一个单例类,它们就都会有各自的实例。
  2. 如果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;    }}