参考文献

单例模式

  • 保证一个类只有一个实例,并提供一个访问该实例的全局节点.

组件

  • 在单例模式中,只有Singleton这一个角色.
  • Singleton角色中有一个返回唯一实例的static方法.该方法总是返回同一个实例.

实现方式

  • 在类中添加一个私有静态成员变量用于保存单例实例.
  • 声明一个公有静态构建方法用于获取单例实例.
  • 在静态方法中实现"延迟初始化". 该方法会在首次被调用时创建一个新对象, 并将其存储在静态成员变量中. 此后该方法每次被调用时都返回该实例.
  • 将类的构造函数设为私有. 类的静态方法仍能调用构造函数, 但是其他对象不能调用.
  • 检查客户端代码, 将对单例的构造函数的调用替换为对其静态构建方法的调用.

具体实现

懒汉式

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

private Singleton() {
...
}

public static synchronized Singleton getInstance(){

if (instance == null)
instance = new Singleton();

return instance;
}

...

public void doSomething()
{
...
}
}
双检锁校验(DCL)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton sLazyDoubleCheckSingleton = null;

private LazyDoubleCheckSingleton() {
}

public static LazyDoubleCheckSingleton getInstance() {

if (sLazyDoubleCheckSingleton == null) {
synchronized (LazyDoubleCheckSingleton.class) {
if (sLazyDoubleCheckSingleton == null) {
// 指令重排序
// 1. 分配内存给这个对象
// 2. 初始化对象
// 3. 设置 sLazyDoubleCheckSingleton 指向刚刚分配的内存地址
sLazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
}
}
}
return sLazyDoubleCheckSingleton;
}
}

饿汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class LazySingleton {
private static LazySingleton sLazySingleton = null;

private LazySingleton() {
if (sLazySingleton != null) {
throw new RuntimeException("单例构造器禁止反射调用");
}
}

/**
* 使用 synchronized 关键字
*
* @return
*/
public synchronized static LazySingleton getInstance() {
if (sLazySingleton == null) {
sLazySingleton = new LazySingleton();
}
return sLazySingleton;
}
}


public class LazySingleton2 {
private static final LazySingleton2 INSTANCE;
//静态代码块
static {
INSTANCE = new LazySingleton2();
}
/**
* 构造函数私有化
*/
private LazySingleton2() { }

public static LazySingleton2 getInstance() {
return INSTANCE;
}
}

静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class StaticInnerClassSingleton {
private static class InnerClass {
private static StaticInnerClassSingleton sStaticInnerClassSingleton = new StaticInnerClassSingleton();
}

private StaticInnerClassSingleton() {
if (InnerClass.sStaticInnerClassSingleton != null) {
throw new RuntimeException("单例构造器禁止反射调用");
}
}

public static StaticInnerClassSingleton getInstance() {
return InnerClass.sStaticInnerClassSingleton;
}
}

枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public enum EnumInstance {
INSTANCE {
@Override
protected void printTest() {
System.out.println("Print Test");
}
};

protected abstract void printTest();

private Object data;

public Object getData() {
return data;
}

public void setData(Object data) {
this.data = data;
}

public static EnumInstance getInstance() {
return INSTANCE;
}
}

与其他模式的关系

  • 外观模式类通常可以转换为单例模式类, 因为在大部分情况下一个外观对象就足够了.
  • 如果你能将对象的所有共享状态简化为一个享元对象, 那么享元模式就和单例类似了. 但这两个模式有两个根本性的不同.
    • 只会有一个单例实体, 但是享元类可以有多个实体, 各实体的内在状态也可以不同.
    • 单例对象可以是可变的. 享元对象是不可变的.
  • 抽象工厂模式、 建造者模式和原型模式都可以用单例来实现.

使用场景

Logger Classes(日志类)

  • 单例模式用于日志类的设计.此类通常作为单例实现,并在所有应用程序组件中提供全局日志记录访问点,而无需在每次执行日志记录操作时创建对象.

Configuration Classes(配置类)

  • 单例模式用于设计为应用程序提供配置设置的类.通过将配置类实现为 Singleton,我们不仅提供了全局访问点,而且还保留了用作缓存对象的实例.当实例化类时(或读取值时),单例会将值保留在其内部结构中.如果从数据库或文件读取值,则可以避免每次使用配置参数时重新加载值.

Accesing resources in shared mode(以共享模式访问资源)

  • 资源管理器
  • 连接池/线程池