Singleton模式

Singleton的意思即是独身,也就是只有一个人,应用在OO上,通常译作单例:只有一个实例(Instance)。
很多时候,你会需要Singleton模式,例如打印机管理,你希望程序中只能有一个PrintSpooler,以避免两个打印动作同时如入到打印机中;又例如数据库管理,因为建立连接对象会消耗资源,你希望程序中只能有一个连接对象,所有的其他程序都透过这个对象来连接数据库,以避免连接对象的重复初始化造成资源的消耗。
以打印机的设计为例,有的设计人员会采用全局变量来建立实例,并在程序中随机去用这个实例,Java虽然不支持全局变量,但透过将对象包装在一个类里面,也有人采用这样的写法:
public class PrintSpooler {
public PrintSpooler() {
// ....
}

public Connection getSpooler(){
....
}
}

public class GlobalObject {
private PrintSpooler printSpooler;
public GlobalObject () {
printSpooler = new PrintSpooler();
...
}

public void getPrintSpooler() {
return printSpooler;
}
}

无乱全局变量还是以上的例子,都无法保证只产生唯一的实例,也许你不会犯这个错误,但与你共同工作的伙伴也许会直觉的使用这种方法来产生一个PrintSpooler实例。

Singleton模式可以保证一个类只有一个实例,并提供一个访问这个实力的方法。
一个Singleton实现好比Java中的java.lang.Runtime类,每个Java程序运行时都有一个唯一的Runtime对象,可以透过它提供的静态方法getRuntime()来取得这个对象,例如:
Runtime runtime = Runtime.getRuntime();

取得Runtime对象之后,你可以透过它尽心一些外部命令的执行、进行垃圾处理等指令,你可以查看Runtime.java类的源码,开头的几行是这样写的:
public class Runtime {
private static Runtime currentRuntime = new Runtime();

public static Runtime getRuntime() {
return currentRuntime;
}

/** Don't let anyone else instantiate this class */
private Runtime() {}

// 以下略
}

以上的结构即采用Singleton模式设计,其结构使用UML来表示如下:
[img]/upload/attachment/89590/58f0ab0c-f151-366b-a5e9-6dc5c21c11c7.jpg[/img]
如上所示,使用静态工厂来取得Runtime对象,其中Runtime的构造函数被声明为private,这样就可以组织其他人使用构造函数来建立实例;使用更一般化的表示单例的UML结构,如下图所示:
[img]/upload/attachment/89592/44715e30-7dd2-3b1f-8a78-b52df3a0b5d1.jpg[/img]

有几个实现以上结构的方法,可以再第一次需要实例是再建立对象,也就是采用所谓的Lazy Initialization:
public class Singleton {
private static Singleton instance = null;

private Singleton() {
// ....
}

public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}

return instance;
}

// .. 其它实现
}

以上实现使用于单线程的程序,在多线程的程序下,以下的写法在多个线程的竞争资源下,将让有可能产生两个以上的实例,例如以下情况:
Thread1: if(instance == null) // true
Thread2: if(instance == null) // true

Thread1: instance = new Singleton(); // 产生一个实例
Thread2: instance = new Singleton(); // 又产生一个实例

Thread1: return instance; // 又返回一个实例
Thread2: return instance; // 又返回一个实例

在多线程的环境下,为了避免竞争资源,加上同步(synchronized)机制:
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
synchronized static public Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

不过这种简单的写法不适合用于像服务器这种多线程的程序上,同步机制会造成相当的性能低下,为了估计Singleton、Lazy Initialization的效率问题,因而有了Double-check Locking的模式:
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if (instance == null){
synchronized(Singleton.class){
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}


Java中的Runtime类的做法就简单得多了,它舍弃了Lazy Initialization,如果你的实例初始化不需要很长时间的话,可以使用这种方式:
public class Singleton {
private static Singleton instance = new Singleton();

private Singleton() {
// ....
}

public static Singleton getInstance() {
return instance;
}

// 其它实现
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值