单例模式:
目的:
确保系统中一个类只产生一个实例。
好处:
1.对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销。
2.由于new操作的次数减少,因而对系统内存的使用频率也会降低,减轻GC压力,缩短GC的停顿时间。
单例模式的角色:
角色 | 作用 |
单例类 | 提供单例的工厂,返回单例 |
使用者 | 获取病使用单例类 |
代码实现:
public class Singleton {
private Singleton(){}
private static Singleton instance = new Singleton();
public static Singleton() {
return instance;
}
}
上述的单例实现,可以确保一个系统中只有一个实例。但是却有另外一个问题,就是由于实例成员变量是static,因此JVM在加载单例类时,单例对象就会被创建。
如果单例类此时还承载着其他用途,那么不管这个单例类是否会被用到,都会初始化这个单例变量。
如下的代码:
public class Singleton {
private Singleton() {
System.out.println("Singleton is create");
}
private static Singleton instance = new Singleton();
private static Singleton getInstance() {
return instance;
} /**
* 模拟单例类中扮演其他角色
*/
public static void createString() {
System.out.println("createString in Singleton");
}
}
当在main方法中尝试调用createString方法时,会看到私有构造中的打印语句被输出了,说明在调用createString时实例化了instance变量。
这也许并不是你想要的结果,如果我们希望对象在使用时再被创建,那么就需要引入延时加载的机制:
public class LazySingleton {
private LazySingleton(){
System.out.println("LazySingleton is create");
}
private static LazySingleton instance = null;
public static synchronized LazySingleton getInstance() {
if(null == instance) {
instance = new LazySingleton();
}
return instance;
} public static void createString() {
System.out.println("createString");
}
}
上述的代码实现了延时加载,同时在获取实例的方法中加入了synchronized关键字,为了确保在多线程环境下的线程安全问题。
但是同样由于synchronized关键字,它的耗时也要远远多于第一种。
为了延时加载,而降低了系统性能有点得不偿失。所以在此改进单例代码:
public class Singleton {
private Singleton(){}
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
上述的代码采用内部类的形式来实现单例代码,首先,单例对象在内部类中进行实例化,而内部类只有在调用getInstance方法时才会被调用一次。从而实现了延时加载。
而且由于java内部类的加载是线程安全的,同时也解决了多线程下单例的问题。