单例模式也叫单子模式,属于创建型模式。
单例模式可分为饱汉(懒汉)式单例和饥汉(饿汉)式单例两种。
单例设计模式是一种非常常见的设计模式,如果整个系统中允许某个类只存在唯一实例,那么就可以使用单例模式。
特别是:
1.初始化这个实例需要加载比较重的资源;
2.该实例要被频繁调用;
3.系统允许只有一个唯一实例;
那么,原则上必须使用单例模式。
单例模式看上去非常简单,但是在饱汉(懒汉)式单例模式的使用中,需要留意线程不同步和不同写法的代码在编译过程产生的单例bug,饥汉(饿汉)式单例则不存在这个问题。
这里推荐一种最规范的Java饱汉式单例模式写法,完全摆脱多线程和代码编译产生的单例
该写法由Google工程师Bob Lee发明,属于懒汉单例模式。解决了饱汉单例模式两大难题。public class SingletonClass{
private SingletonClass(){} ;
private static class SingletonClassInstance{
private static final SingletonClass instance = new SingletonClass() ;
}
public static SingletonClass getInstance(){
return SingletonClassInstance.instance ;
}
}
1.饿汉式单例模式:
public class SingletonClass{
private SingletonClass(){} ;
private static SingletonClass instance = new SingletonClass() ;
public static SingletonClass getInstance(){
return instance ;
}
}
看代码,这个实例是静态的,且在系统初始化的时候就已经初始化,并且永久存在。这就是饿汉式单例。因为是系统初始化的时候就存在,所以该实例不会因为线程的调度产生,所以不会存在线程安全问题。并且代码结构简约严谨,不会因为编译过程中的代码调优而改变代码的执行顺序,所以也不会产生编译bug。
下面看饱汉式单例的举例,我们通常所说的惰性加载(lazy load),程序初始化的时候单例实例并不初始化,需要等线程调用的时候才初始化这个实例。
2.饱汉式单例模式:public class SingletonClass{
private SingletonClass(){} ;
private static SingletonClass instance = null ;
public static SingletonClass getInstance(){
if(instance == null){
instance = new SingletonClass() ;
}
return instance ;
}
}
在多线程环境中,CPU发生时间片切换,两个或多个线程有可能几乎是同一时间执行的,那么,这个实例同时被认为是null,就会多次被创建,虽然可能对性能的影响微乎其微,但是已经违反了单子模式的设计初衷。
如何既要使用懒汉式单例模式,又要保证线程安全?很多蹩脚的程序员自然想到了使用线程阻塞(synchronized关键字)。这很可笑,synchronized本来就对性能有很大影响,还有必要使用懒汉式单例吗?
下面我们给懒汉式单例加个锁:
public class SingletonClass{private SingletonClass(){} ;
private static SingletonClass instance = null ;
public synchronized static SingletonClass getInstance(){
if(instance == null){
instance = new SingletonClass() ;
}
return instance ;
}
}
这样性能不好,换个好点的加锁方式:
public class SingletonClass{
private SingletonClass(){} ;
private static SingletonClass instance = null ;
public static SingletonClass getInstance(){
synchronized(SingletonClass.class){
if(instance == null){
instance = new SingletonClass() ;
}
}
return instance ;
}
}
看上去性能好像好多了,但是还是不够好:
public class SingletonClass{
private SingletonClass(){} ;
private static SingletonClass instance = null ;
public static SingletonClass getInstance(){
if(instance == null){
synchronized(SingletonClass.class){
if(instance == null){
instance = new SingletonClass() ;
}
}
}
return instance ;
}
}
这样看上去完美了。这就是双锁实现单子模式。
枚举
public enum Singleton2 {
INSTANCE ;
public void whatevermethod(){
}
}
这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。