单例模式可以保证一个类只有一个实例,本文讲述Java的多种实现。

  • 目的
    单例模式可以保证一个类只有一个实例,并提供一个访问它的全局访问点。什么情况下需要一个类只能创建一个实例这种情况呢?举一些例子,操作系统只能有一个文件系统,一个窗口管理器。

  • 方法
    为了实现单列模式,我们可以让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建(通过截取创建新对象的请求),并且它可以提供一个访问该实例的方法。

  • 使用
    当类只能有一个实例而且调用端可以从一个公共入口访问。
    作为父类,用来扩展其他只能唯一实例化的类

  • 注意
    注意创建过程中的线程安全性。由于该模式需要一个静态变量来保存唯一的实例,为了线程安全需要改静态变量的线程安全。下面的实现保证线程安全采用加锁的方法。

  • 单例模式中的主动实例化和被动实例化
    主动实例化指在加载主类的时候就实例化单例类,并保存到类的静态变量中,它是线程安全的。被动实例化指在首次调用单例类静态方法或构造器时创建。问题:Python可以实现主动实例化吗?

Java中的立即加载/延迟加载

立即加载的实现

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

//立即加载
public class Singleton {
private static Singleton singleton = new Singleton();

private Singleton() {
}

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

延迟加载的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 延迟加载
public class Singleton {
private static Singleton singleton;

private Singleton() {
}

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

下面是Java的实现方法,根据Java的语法特性实现,在语法上有差异,但原理一样

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

private static Singleton instance;

private Singleton() {
}

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

线程安全的实现,只需要在getInstance上加上同步语句synchronized就可以。

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中主动实例化代码示例,线程安全的

1
2
3
4
5
6
7
8
9
10
11
12
public Singleton {
private static Singleton instance = new Singleton(); //提前创建
private Singleton() {
}

private Singleton() {
}

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

采用内部类的方式。静态内置类可以解决单例模式在多线程下线程不安全的情况。

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private static class Inner {
private static Singleton obj = new Singleton();
}

private Singleton() {
}

public static Singleton getInstance() {
return Singleton.obj;
}
}

使用static代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pulbic class Singleton {
private static Singleton instance = null;

private Singleton() {
}

static {
instance = new Singleton();
}

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

此外,基于Java的枚举enum实现的线程安全单例模式。

总结

设计模式的基本实现方法和面向对象思想、编程语言的设计特性息息相关。

  • 在类创建时,在类内部用一个静态变量保存该类创建的实例(立即加载)
  • 依然用一个静态变量保存类的实例,但是在需要的时候才调用静态方法创建。这个方法需要考虑线程安全问题,设置同步。可以直接对代码块同步,但效率低。可以考虑用一个锁和二次检测方法。DCL双检测锁方法。
  • 使用静态内置类
  • 使用static代码块
  • 使用Enum类创建单例模式例模式