一、单例模式的陷阱
设计模式中的单例模式应该是被大家使用最广泛的模式之一,但网上关于单例模式的诟病也不少,最集中的就是:在多线程的环境下,单例模式有可能返回不止一个的对象。那么到底为什么会出现这种情况呢?下面我们来看单例模式的两种实现方式
方式一
public class Singleton {
//注意构造方法必须是私有的
private Singleton(){}
//在自己内部定义自己一个实例,是不是很奇怪?
//注意这是private 只供内部调用
private static Singleton instance = new Singleton();
//这里提供了一个供外部访问本class的静态方法,可以直接访问
public static Singleton getInstance() {
return instance;
}
}
方式二
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
//这个方法比上面有所改进,不用每次都进行生成对象,只是第一次
//使用时生成实例!
if (instance==null)
instance=new Singleton();
return instance;
}
}
}
方式二就是我们说的:滞后初始化(Lazy Initialization)。为什么会有滞后初始化这种实现方式出现呢?我们可用看到在第一种实现方式中无法向单例模式的构造方法传递参数,而使用滞后初始化的方式,我们可用在调用getInstance()方法的时候向方法中传递参数。
凡事有好处必然有坏处,滞后初始化的一个弊病就是在多线程或分布式的环境下有可能出现混乱:
“有时在某些情况下,使用Singleton并不能达到Singleton的目的,如有多个Singleton对象同时被不同的类装入器装载;在EJB这样的分布式系统中使用也要注意这种情况,因为EJB是跨服务器,跨JVM的。” --摘自www.jdon.com-《GoF 23种设计模式解析》
“在多线程环境下,我们无法保证一个方法能够持续运行到结束,其他线程的方法才开始运行。因而可能存在这样一种情形:两个线程几乎同时尝试初始化单例类。假设第一个方法发现单例为空,而第二个方法在此刻开始运行,它也会发现该单例为空。接下来,这两个方法都将对该单例进行初始化。” --摘自《Java设计模式》
二、单例模式在多线程下的安全实现
《Java并发编程》一书建议使用属于当前类的锁进行同步,代码如下:
public class Singleton {
private static Singleton instance = null;
// 注意这里的static非常重要,如果为对象变量则因为存在多份拷贝而起不到限制的作用
private static Object classLock = Singleton.class;
public static Singleton getInstance() {
//这个方法比上面有所改进,不用每次都进行生成对象,只是第一次
//使用时生成实例!
synchronized(classLock){
if (instance==null)
instance=new Singleton();
return instance;
}
}
}
}
在第一个线程开始滞后初始化的时候,如果有另一线程也准备开始初始化,这时候,第二个线程将停止执行,等待获取对象classLock的锁。当第二个线程获取这个锁并开始执行初始化的时候,它会发现该单例已不再为空(因为只存在该类的唯有实例,我们可以使用单个静态锁)
三、使用单例模式的另外一些注意点
·单例模式类不能实现Clonable接口,以防被克隆而产生多个实例
·单例模式类不能实现Serializable接口,以防被序列化而产生多个实例
-------------------------------------------------------------
生活就像打牌,不是要抓一手好牌,而是要尽力打好一手烂牌。
posted on 2008-01-03 22:31
Paul Lin 阅读(1194)
评论(0) 编辑 收藏 所属分类:
模式与重构