设计模式(1)——单例模式

本文介绍单例模式的概念和应用。

基本思想和原则

保证一个类仅有一个实例,自行实例化并提供一个访问它的全局访问点。

单例模式有以下三个要素:

  1. 保证类实例的唯一性。
  2. 提供一个获取类唯一实例的全局访问点。
  3. 类必须自行创建这个唯一的实例,不能由用户手动创建。

动机

在一些情况下我们希望某个类在系统中有且仅有一个实例,常见的例子有全局id生成器、任务管理器、全局计时器、配置对象等。如果这些类的对象有多个,可能造成数据不一致的情况,因此需要一种通用的模式来保证这些类在系统中有且仅有一个实例。

实现

单例模式的实现可以分为懒汉式和饿汉式两种,懒汉式指的是等到需要的时候再创建对象,饿汉式则是在类被加载时就创建一个对象。因此懒汉式存在线程安全的问题,而饿汉式没有线程安全问题。

我们先来看懒汉式:

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

private static Singleton _instcance = null;

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

上面的方式不是线程安全的,如果想要线程安全,可以使用同步:

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

private static Singleton _instcance = null;

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

这种方式将整个getInstance方法同步了,因此每次调用的时候都要同步,效率比较低。还可以只在第一次的时候同步:

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

private static Singleton _instcance = null;

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

}
return _instcance;
}
}

上面这种方式只在第一次的时候同步,当已经存在实例时直接返回。

还可以使用静态内部类实现单例:

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

private static class InnerClass{
private static final Singleton _instance = new Singleton();
}
private Singleton() {}

public static Singleton getInstance() {
return InnerClass._instance;
}
}

静态内部类也不需要同步,因为在类加载的时候就创建了实例。

主函数如下:

1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
Singleton obj1 = Singleton.getInstance();
Singleton obj2 = Singleton.getInstance();
System.out.println(obj1.hashCode());
System.out.println(obj2.hashCode());
}
}

打印的结果是obj1obj2hashCode相同(每次运行得到的hashCode不同):

1
2
1761291320
1761291320