使用单例模式可以省略创建对象所花费的时间,而且由于new操作的次数减少,因而对系统内存使用频率降低,减轻了GC的压力,缩短了GC的停顿时间。
public class Singleton {
private Singleton(){
System.out.println("Singleton is creating");
}
private static StaticSingleton instance=new StaticSingleton();
public static StaticSingleton getInstance(){
return instance;
}
}
然而上诉单例模式可能会问题:instance是在static中定义的,因此在JVM加载单例类的时候,单例对象就会被建立,但这个单例类有可能还会有其他方法,在使用其他方法时加载此单例类也会创建单例对象,这是不愿意见到的。因此引入了延迟加载机制:
public class LazySingleton {
private LazySingleton(){
System.out.println("Singleton is creating");
}
private static LazySingleton instance=null;
public static synchronized LazySingleton getInstance(){
if(instance==null){
instance=new LazySingleton();
}
return instance;
}
}
可以看到,初始化单例是在同步方法中进行的,同步是为了避免多线程访问时创建多个单例对象,比如一个线程刚判断了instance==null,还在创建单例时,另一个线程可能也判断instance==null,也开始创建单例。但这样引入了新的问题,同步关键字的耗时远远高于第一种单例模式。
因此引入了一种新的方法:通过内部类维护单例:
public class StaticSingleton {
private StaticSingleton(){
System.out.println("Singleton is creating");
}
private static class SingletonHolder{
private static StaticSingleton instance=new StaticSingleton();
}
public static StaticSingleton getInstance(){
return SingletonHolder.instance;
}
}
因为内部类在StaticSingleton 被加载时时不会被初始化的,只有在getInstance()方法被调用的时候,才回加载内部类,从而初始化instance。
另外,如果单例类实现了java.io.Serializabel,则在序列化和反序列化后会生成多个对象实例,此时,需要在类中实现readResolve()方法。