图1 单例模式模型
在很多操作中,比如建立目录 数据库连接都需要这样的单线程操作。一些资源管理器常常设计成单例模式。例如,Windows 回收站,在整个视窗系统中,回收站只能有一个实例,整个系统都使用这个惟一的实例,而且回收站自行提供自己的实例。因此,回收站是单例模式的应用。
2 单例模式之实现思路
实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。
3 单例模式之实现方法
3.1单例模式之JAVA实现
我们知道java中的构造函数的访问权限是可以有3种public、protected、private,但是我们要实现单例模式,通常我们不把它设为public类型,通常设置为protected或private类型,也许有人会觉得就只应该设置为private类型。这里设置为protected类型主要是为了能让子类能继承,使子类能够依靠父类实例化。在java中主要有三种设计方法:饿汉模式、懒汉模式和登记模式。
饿汉模式:指全局的单例实例在类装载时构建,其实现代码如下:
package com.cstc.org.guang;
public class EagerSingleton {
private static final EagerSingleton m_instance =
new EagerSingleton();
private EagerSingleton(){}
public static EagerSingleton getInstance(){
return m_instance;
}
}
在此类中,主要是在这个类被加载时静态变量会被初始化,此时唯一的对象就创建出来了
懒汉模式:指全局的单例实例在第一次被使用时构建,其实现代码如下:
package com.cstc.org.guang;
public class LazySingleton {
private static LazySingleton m_instance=null;
private LazySingleton(){}
synchronized public static LazySingleton getInstance(){
if(m_instance==null){
m_instance=new LazySingleton();
}
return m_instance;
}
}
在这种方式中只有当我们在第一次调用了getInstance()方法时才创建唯一实例,同时我们用了关键字synchronized,主要是为了适应多线程环境。
登记模式:此模式实际上维护的是一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从工厂直接返回,对于没有登记的,则先登记,而后返回。其实现代码如下:
package com.cstc.org.guang;
public class RegSingleton {
/*** 登记薄,用来存放所有登记的实例 */
private static Map<String, RegSingleton> m_registry = new HashMap();
//在类加载的时候添加一个实例到登记薄
static {
RegSingleton x = new RegSingleton();
m_registry.put(x.getClass().getName(), x);
}
/*** 受保护的默认构造方法*/
protected RegSingleton() {}
/*** 静态工厂方法,返回指定登记对象的唯一实例;
* 对于已登记的直接取出返回,对于还未登记的,先登记,然后取出返回
* @param name
* @return RegSingleton
*/
public static RegSingleton getInstance(String name) {
if (name == null) {
name = "RegSingleton";
}
if (m_registry.get(name) == null) {
try {
m_registry.put(name, (RegSingleton) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return m_registry.get(name);
}
/*** 一个示意性的商业方法
* @return String
*/
public String about() {
return "Hello,I am RegSingleton!";
}
}
这一方法克服了前面两种不可继承的缺点,接着我们看看它的子类的一个例子:
package com.cstc.org.guang;
public class RegSingleChild extends RegSingleton {
public RegSingleChild(){}
public static RegSingleChild getInstance(){
return (RegSingleChild)RegSingleton.getInstance("com.cstc.org.guang.RegSingleChild");
}
public String about(){
return "Hello,I am RegSingleChild";
}
}
由于我们这的子类必须允许父类以构造函数调用产生实例,因此我们这里的子类的构造函数必须是public类型。