0%

单例模式.md

[TOC]

单例模式(Singleton),也叫单子模式,是一种常用的软件设计模式。
在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。
比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。
这种方式简化了在复杂环境下的配置管理。

适用场景

单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如:

  1. 需要频繁实例化然后销毁的对象。
  2. 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
  3. 有状态的工具类对象。
  4. 频繁访问数据库或文件的对象。

经典使用场景

  1. 外部资源:每台计算机有若干个打印机,但只能有一个 Printer Spooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件
  2. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧)
  3. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
  4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
  5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
  6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
  7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,能节省打开或者关闭数据库连接的效率损耗,这种效率上的损耗还是非常昂贵的,因为用单例模式来维护,就可以大大降低这种损耗。
  8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
  9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
  10. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例.

懒汉式,线程不安全

这种方式是最简单的实现方式,能够实现懒加载。

最大的缺点:不支持多线程。

1
2
3
4
5
6
7
8
9
10
11
12

public class Singleton {
private static Singleton instance;
private Singleton (){}

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

懒汉式,线程安全

具备懒加载,同时由于有synchronized的存在,保证了线程安全。

缺点:加锁影响效率,任何时候只能有一个线程调用getInstance()。

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {
private static Singleton instance;
private Singleton (){}

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

饿汉式,线程安全

利用classloader机制避免线程安全。

优点:没有加锁,效率高
缺点:不具备懒加载。对于依赖配置文件的单例类,这种方法无法完成。

1
2
3
4
5
6
7
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}

双重校验锁

采用双锁机制,安全且在多线程情况下能保持高性能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

静态内部类

使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。

1
2
3
4
5
6
7
8
9
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

枚举 Enum

创建枚举默认就是线程安全的,所以不需要担心double checked locking,而且还能防止反序列化导致重新创建新的对象

1
2
3
public enum EasySingleton{
INSTANCE;
}

单例模式