最近突然想回顾一下设计模式,很多东西是要回过头来总结一下的。今天先回顾一下单例吧。
很多时候觉得挺搞笑的,去面试的时候如果人家问你设计模式,一般都是要你写个单例模式。去年来北京好几家面试都是问我这个。当时我就想这个能反映出一个人的水平来吗?还是说更多的是反映出这个公司的水平呢?
随着一年的应用,很多地方都用过之后觉得,单例这个东西虽然简单,可是现实是复杂的。所以单例这个简单的模式也不能太小瞧咯。
单例其实有很多种实现,这是其中的一种,延迟加载的(好像英文叫Lazy?):
[下面代码中所有的构造器都是私有的,这里我就省略不写了。]
public class ClassName {
public static ClassName getInstance(){
if(instance == null)
{
instance = new ClassName();
}
return instance;
}
private static ClassName instance;
}
这种的好处是我们的单例使用时才进行初始化,这样方便我们在系统启动时做些小动作。但是这个方式不是线程安全的,想要完成一个线程安全的单例,有几种方式:
(一)
public class ClassName {
public static ClassName getInstance(){
return instance;
}
private static ClassName instance = new ClassName();
}
这种方式,可以保证我们的单例是线程安全的,毕竟我们唯一的实例在系统初始化的时候就构造了。可是Java的机制是static级别的变量初始化时互相调用是会报异常的。所以随着系统的扩展,尤其还会有一些新手或者粗心大意的家伙(比如说,我)会乱用你的方法。一不小心就造成问题了。而且,你也失去了第一个方式中的一个小优势,不能在系统启动时做点小动作了。
(二)
public class ClassName {
public static synchronized ClassName getInstance(){
if(instance == null)
{
instance = new ClassName();
}
return instance;
}
private static ClassName instance;
}
这样倒是线程安全了,也可以延迟加载,但是从今以后这个getInstance方法就是
synchronized的了,那绝对是很影响效率的。我跟朋友讨论提出了几种写法,以期既能使单例可以在系统启动不至于数据已经煮成熟饭又是线程安全的:(少数人讨论结果,代码可能会比较丑陋,仅供参考,欢迎拍砖)
public class ClassName {
public static ClassName getInstance(){
if(instance == null)
{
instance = ClassName.createInstance();
}
return instance;
}
private static synchronized ClassName createInstance(){
if(instance == null)
{
return new ClassName();
}else{
return instance;
}
}
private static ClassName instance;
}
这种写法就很好的解决了这些问题。
还有一种写法是这样的,这个不是延迟加载的。而是采用了一种取巧的方式。
public class ClassName {
public static ClassName getInstance(){
if(!instance.isInit)
{
instance.initSingleton();
}
return instance;
}
private synchronized void initSingleton() {
if(!isInit)
{
reset();//这名字是有点怪异,我没时间想太好听的名字
isInit = true;
}
}
public void reset(){
//.....真正进行数据初始化的地方
}
private boolean isInit = false;
private static ClassName instance = new ClassName();
}
将所有的初始化代码搬到构造器之外。这是专为数据初始化和复位进行的设计。所以我把reset开放了出来。