问题:设计一个类,该类只能生成一个实例。

原理:

单例模式属于设计模式中的创建模式,它可以确保一个类只有一个实例。有这种需求有多种原因,包括资源控制、权限管理和全局对象等。在不考虑具体的实现语言情况下,实现单例模式的一种通用思路是:创建一个_sentinel变量,用以标识类是否已经创建了实例。该变量可以是布尔类型或引用类型。出于并发安全的考虑,可以在_sentinel变量上加锁,以确保一次只有一个线程能修改该变量。类似地,采用哨兵_sentinel变量思想的模式还有消费者/生产者模式,该模式通过哨兵变量实现生产、消费的终止通信。具体见历史文章

除上述的常规思路外。对于不同的语言,由于语言间语法特性的差异,导致在实现上有很大的差别。

实现:

思路一:

使用静态变量保存单例类的实例。

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

private static Singleton instance;

private Singleton() {
}

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

这种思路是线程不安全的。

思路二:

线程安全的实现,但效率不高。

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

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

return instance;
}
}

但这种方法的成本太高。每次调用该方法都要加锁,如果类本身已经存在实例了,就没有加锁的必要。

思路三:

在第二种思路下可以采用一种高效的方法:在加锁前后分别做实例检测。第一次检测判断是否实例已经被创建了,如果创建了就没有必要再加锁,返回保存的实例。否则进入第二次检测。加锁后进行第二次检测是为了检测在第一次检测都加锁之间的时间片上有其他线程已经创建了实例,如果其他线程创建了实例,就不能再创建了,否则创建实例,而加锁本身就确保该创建部分的执行是同步的、唯一的。通过第三种思路优化。

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

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

推广:Java中,在不使用synchronizedlock的情况下如何实现线程安全的单例模式?详细的解答见实现Singleton模式(算法2+)

完。