单件模式
单件模式是可以用来创建独一无二的,只能有一个的对象。
单件模式的类图可以说是最简单的,事实上它的类图只有一个类,但是也不要把它想的那么简单,因为它也是需要很多限制才能够安全的运行起来。
首先我们来看一个经典的单件模式的代码:
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
public static Singleton getInstance(){
if (singleton == null){
return new Singleton();
}
return singleton;
}
//其他方法
}
构造器设置为私有的,这样只能类的内部创建对象了。外部需要得到该对象的话,就必须调用getInstance方法来获取,而getInstance里面判断了如果singleton对象为空则创建这个对象,否则返回该对象,这样就创造了单件的对象了。
单件模式与之间命名一个静态对象相比有一个优点,就是能够延迟实例化。不会在项目加载的时候就实例化了对象。
单件模式:确保一个类只有一个实例,并提供一个全局访问点。
简单单件模式的缺点
但是对于简单的单件模式,在多线程的情况下,并不适用的。如下图是两个线程同时请求单件对象:
这样就有两个单件对象了。这可能会让系统存在无法发现的问题,怎么查也查不到。
解决方法,使用同步代码块:
public class Singleton {
private static Singleton singleton;
private Singleton(){}
public static synchronized Singleton getInstance(){
if (singleton == null){
return new Singleton();
}
return singleton;
}
//其他方法
}
但是使用同步代码块的话,就会造成性能上的问题,这就引申出了另外一个问题了。而且单件模式的这个代码块,只是在创建对象的时候有用,之后每次调用都成为了累赘了。
能够改善多线程吗?
1.如果getInstance的性能对程序不是关键的,那么就什么都不用管。
对于不是程序的关键,使用同步代码块,既简单又有效。但是可能会造成效率下降100倍。
2.急切的初始化对象,而不用懒加载的方法。
如果程序总是使用单件,并且程序在启动时负担不重,可以考虑使用立即初始化单件对象。
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton(){}
public static synchronized Singleton getInstance(){
return singleton;
}
//其他方法
}
这个方法依赖了JVM加载这个类时就创建了一个单件对象。JVM保证在任何线程访问之前就创建好了对象。
3.用“双重检查加锁”在getInstance()中减少同步的使用。
public class Singleton {
//volatile 只适用于java5以上的版本哦,volatile能保证对象被初始化完成之后能够正确处理多线程
private volatile static Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if (singleton == null){
synchronized (Singleton.class) {
if (singleton == null) {
return new Singleton();
}
}
}
return singleton;
}
//其他方法
}
如果性能是你关注的,那么使用这种方法会大大减少getInstance的时间消耗。
总结
有些对象我们只需要一个,比方说:线程池、缓存、对话框、注册表等等。这时候不妨使用单件模式来实现。
单件模式:确保一个类只有一个实例,并提供一个全局访问点。