Singleton

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

单线程版本

懒汉模式 & 饿汉模式

懒汉模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton {

// 定义一个空的 Singleton 对象引用 SINGLETON
private static Singleton SINGLETON = null;

// 利用构造器私有化来限制外部使用 new 关键字,进而避免构造出多个实例
private Singleton() {
}

// 提供外部获取单例对象的方法
public static Singleton getInstance() {
// 如果 SINGLETON 还未指向任何一个 Singleton 对象,new 一个 Singleton 对象出来。
if (SINGLETON == null ) {
SINGLETON = new Singleton();
}
return SINGLETON;
}
}

对象引用一开始为 null,具体使用的时候才 new 一个对象出来,这种写法属于懒汉模式。

饿汉模式

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {

// 在定义时就直接 new 出单例对象
private static Singleton SINGLETON = new Singleton();

private Singleton() {
}

public static Singleton getInstance() {
return SINGLETON;
}
}

顾名思义,单例对象一开始就被 new Singleton() 主动构建,不再需要判空操作,这种写法属于饿汉模式。

多线程版本

如果上面『懒汉模式』的代码是运行在多线程环境中,就会出现许多问题。最明显的一个,将有可能 new 出多个 Singleton 对象实例。最简单的修复方式是在 getInstance() 方法上加 synchronize 关键字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton {

private static Singleton SINGLETON = null;

private Singleton() {
}

// 方法上加 synchronized 关键字
public synchronized static Singleton getInstance() {
if (SINGLETON == null ) {
SINGLETON = new Singleton();
}
return SINGLETON;
}
}

但是这样的话,不管单例对象有没有构建出来,每次调用 getInstance() 方法的时候都会加锁。 使用 double-checked lock(双检锁) 可以避免在 new 出单例对象之后还对 getInstance() 方法进行加锁,这是对锁粒度细化的一种体现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Singleton {

private static Singleton SINGLETON = null;

private Singleton() {
}

public static Singleton getInstance() {
// 第一次检查
if (SINGLETON == null) {

// 只有 SINGLETON 为 null 时,才对 new Singleton() 代码块进行加锁。
synchronized (Singleton.class) {
// 第二次检查
if (SINGLETON == null) {
SINGLETON = new Singleton();
}
}

}
return SINGLETON;
}
}

引用