什么是单例模式?
单例模式:确保一个类只有一个实例,并提供一个全局访问点。
使用场景:
线程池(threadpool),缓存(cache),对话框,处理偏好设置和注册表(registry)的对象,日志对象,充当打印机,显卡等设备的驱动程序的对象。 事实上这类对象只能有一个实例,如果制造出多个实例,就会导致许多问题产生。例如:程序的行为异常、资源使用过量,或者是不一致的结果。
经典单例模式实现:
public class Singleton{
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getSingleton() {
if (uniqueInstance== null) {
uniqueInstance = new Singleton();
}
return uniqueInstance ;
}
//其它有用的方法
}
咋一看上面代码已实现单例模式定义的两个条件: 1.只有一个实例(private static SingleTon uniqueInstance) 2.提供了一个全局访问点( public static getSingleton() )
问题来了:当是多线程的情况怎么办呢?具体问题就不多说了,就是代码方法调用的先后顺序,对应的业务就不对了。
大伙可能立马想到的是加synchronized同步方法,是了只要把getInstance()变成同步(synchronized)方法,多线程灾难就可以轻而易举的解决了(代码如下):
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static synchronized Singleton getSingleton() {
if (uniqueInstance== null) {
uniqueInstance = new Singleton();
}
return uniqueInstance ;
}
//其它有用的方法
}
看到这块儿,可能有的同学会说了,同步getInstance()的做法交拖垮性能,该怎么办呢?
1.如果getInstance()的性能对应用程序不是很关键,就什么都别做
2.使用“急切”创建实例,而不用延迟实例化的做法
public class Singleton {
private static Singleton uniquerInstance = new Singleton();
private Singleton(){}
public static Singleton getInstance() {
return uniqueInstance;
}
}
利用这个做法,我们依赖JVM在加载这个类时,马上创建此唯一的实例。JVM保证在任何线程访问uniqueInstance静态变量之前,一定先创建此实例。
3.用”双重检查加锁“,在getInstance()中减少使用同步
利用双重检查加锁(double-checked locking),首先检查是否实例已经创建了,如果尚未创建,“才”进行同步。
public class Singleton {
private volatile static Singleton uniquerInstance = new Singleton();
private Singleton(){}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniquerInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
如果性能是你关心的重点,那么这个做法可以帮你大大地减少getInstance()的时间耗费。
注意:
双重检查加锁不适用于1.4及更早版本的Java!
在1.4及更早版本的java中,许多JVM对于volatile关键字的实现会导致双重检查加锁的失效。如果你不能用Java5,而必须使用旧版的Java,就请不要利用此技艺实现单例模式。