单例模式有三个特点:
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给所有其他对象提供这一实例
一般Singleton模式通常有两种形式:
第一种形式:也是常用的形式:
public class Singleton{
private static Singleton instance = null;
private Singleton(){
// do something
}
public static Singleton getInstance(){
if(instance == null) { instance = new Singleton();}
/**这个方法比第二个有所改进,不用每次都进行生成对象,只是第一次使用时生成实例 */
// 懒汉模式,它的特点是运行时获得对象的速度比较慢,但加载类的时候比较快。它在整个应用的生命周期只有一部分时间在占用资源
return instance;
}
}
第二种形式:
public class Singleton{
private static final Singleton instance = new Singleton(); // 在自己内部定义自己的一个实例,只供内部调用
// 饿汉模式,它的特点是加载类的时候比较慢,但运行时获得对象的速度比较快。它从加载到应用结束会一直占用资源
private Singleton(){
// do something
}
public static Singleton getInstance(){
/**这里提供了一个供外部访问本class的静态方法,可以直接访问 */
return instance;
}
}
什么情况下使用单例模式?
单例模式也是一种比较常见的设计模式,有三个方面的作用:
- 控制资源的使用,通过线程同步来控制资源的并发访问;
- 控制实例产生的数量,达到节约资源的目的。
- 作为实例产生的数量,也就是数据共享,它可以在不建立直接关联的条件下,让多个不相关的两个线程或者进程之间实现通讯。
比如,数据库连接池的设计一般采用单例模式,数据库连接是一种数据库资源。软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的。当然,使用数据库连接池还有很多其它的好处,可以屏蔽不同数据数据库之间的差异,实现系统对数据库的低度耦合,也可以被多个系统同时使用,具有高可复用性,还能方便对数据库连接的管理等等。数据库连接池属于重量级资源,一个应用中只需要保留一份即可,既节省了资源又方便管理。所以数据库连接池采用单例模式进行设计会是一个非常好的选择。
在我们日常使用的在Windows中也有不少单例模式设计的组件,象常用的文件管理器。由于Windows操作系统是一个典型的多进程多线程系统,那么在创建或者删除某个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象。采用单例模式设计的文件管理器就可以完美的解决这个问题,所有的文件操作都必须通过唯一的实例进行,这样就不会产生混乱的现象。
再比如,每台计算机可以有若干个打印机,如果每一个进程或者线程都独立地使用打印机资源的话,那么我们打印出来的结果就有可能既包含这个打印任务的一部分,又包含另外一个打印任务的一部分。所以,大多数的操作系统最终为打印任务设计了一个单例模式的假脱机服务Printer Spooler,所有的打印任务都需要通过假脱机服务进行。
实际上,配置信息类、管理类、控制类、门面类、代理类通常被设计为单例类。像Java的Struts、Spring框架,.Net的Spring.Net框架,以及Php的Zend框架都大量使用了单例模式。
总结:单例模式能够保证一个类仅有唯一的实例,并提供一个全局访问点。使用单例模式最核心的一点是体现了面向对象封装性中的“单一职责”和“对象自治”原则。
附实例: 单例模式中需要解决的重要问题是方法的同步问题,同步的粒度有多大等。在本例子中同在获得类的实例的时候使用了同步,代码如下:
public class Singleton{
private Singleton(){
generator = new Random();
}
public void setSeed(int seed) {
generator.setSeed(seed);
}
public int nextInt() {
return generator.nextInt();
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
private Random generator;
private static Singleton instance;
}
客户端调用的代码:
public class Client{
public static void main(String[] args){
Singleton s1 = Singleton.getInstance();
System.out.println(s1.toString());
for ( int i=0;i<10;i++){
Singleton s2 = Singleton.getInstance();
System.out.println(" The randomed number is " +s2.toString());
}
}
}